[asterisk-commits] res pjsip exten state: Add config support for exten state pu... (asterisk[master])

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Apr 28 15:35:09 CDT 2016


Anonymous Coward #1000019 has submitted this change and it was merged.

Change subject: res_pjsip_exten_state: Add config support for exten state publishers.
......................................................................


res_pjsip_exten_state: Add config support for exten state publishers.

This change adds the ability to configure outbound publishing of
extension state. Right now stuff is merely set up to store the
configuration and to register a global extension state callback. The
act of constructing the body and sending is not yet complete.

Configurable elements right now are a regex for filtering the context,
a regex for filtering the extension, and the body type to publish.

ASTERISK-25922 #close

Change-Id: Ia7e630136dfc355073c1cadff8ad394a08523d78
---
M res/res_pjsip_exten_state.c
1 file changed, 294 insertions(+), 9 deletions(-)

Approvals:
  Richard Mudgett: Looks good to me, but someone else must approve
  Anonymous Coward #1000019: Verified
  Joshua Colp: Looks good to me, approved



diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index a4ad1cd..1f8b121 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -20,16 +20,20 @@
 	<depend>pjproject</depend>
 	<depend>res_pjsip</depend>
 	<depend>res_pjsip_pubsub</depend>
+	<depend>res_pjsip_outbound_publish</depend>
 	<support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
+
+#include <regex.h>
 
 #include <pjsip.h>
 #include <pjsip_simple.h>
 #include <pjlib.h>
 
 #include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_outbound_publish.h"
 #include "asterisk/res_pjsip_pubsub.h"
 #include "asterisk/res_pjsip_body_generator_types.h"
 #include "asterisk/module.h"
@@ -41,6 +45,16 @@
 
 #define BODY_SIZE 1024
 #define EVENT_TYPE_SIZE 50
+
+/*!
+ * \brief The number of buckets to use for storing publishers
+ */
+#define PUBLISHER_BUCKETS 31
+
+/*!
+ * \brief Container of active outbound extension state publishers
+ */
+static struct ao2_container *publishers;
 
 /*!
  * \brief A subscription for extension state
@@ -68,6 +82,29 @@
 	enum ast_presence_state last_presence_state;
 };
 
+/*!
+ * \brief An extension state publisher
+ *
+ */
+struct exten_state_publisher {
+	/*! Regular expression for context filtering */
+	regex_t context_regex;
+	/*! Regular expression for extension filtering */
+	regex_t exten_regex;
+	/*! Publish client to use for sending publish messages */
+	struct ast_sip_outbound_publish_client *client;
+	/*! Whether context filtering is active */
+	unsigned int context_filter;
+	/*! Whether extension filtering is active */
+	unsigned int exten_filter;
+	/*! The body type to use for this publisher - stored after the name */
+	char *body_type;
+	/*! The body subtype to use for this publisher - stored after the body type */
+	char *body_subtype;
+	/*! The name of this publisher */
+	char name[0];
+};
+
 #define DEFAULT_PRESENCE_BODY "application/pidf+xml"
 #define DEFAULT_DIALOG_BODY "application/dialog-info+xml"
 
@@ -77,6 +114,9 @@
 static void *get_notify_data(struct ast_sip_subscription *sub);
 static void to_ami(struct ast_sip_subscription *sub,
 		   struct ast_str **buf);
+static int publisher_start(struct ast_sip_outbound_publish *configuration,
+			   struct ast_sip_outbound_publish_client *client);
+static int publisher_stop(struct ast_sip_outbound_publish_client *client);
 
 struct ast_sip_notifier presence_notifier = {
 	.default_accept = DEFAULT_PRESENCE_BODY,
@@ -101,6 +141,12 @@
 	.notifier = &presence_notifier,
 };
 
+struct ast_sip_event_publisher_handler presence_publisher = {
+	.event_name = "presence",
+	.start_publishing = publisher_start,
+	.stop_publishing = publisher_stop,
+};
+
 struct ast_sip_subscription_handler dialog_handler = {
 	.event_name = "dialog",
 	.body_type = AST_SIP_EXTEN_STATE_DATA,
@@ -108,6 +154,12 @@
 	.subscription_shutdown = subscription_shutdown,
 	.to_ami = to_ami,
 	.notifier = &dialog_notifier,
+};
+
+struct ast_sip_event_publisher_handler dialog_publisher = {
+	.event_name = "dialog",
+	.start_publishing = publisher_start,
+	.stop_publishing = publisher_stop,
 };
 
 static void exten_state_subscription_destructor(void *obj)
@@ -490,31 +542,264 @@
 			       exten_state_sub->last_exten_state));
 }
 
+/*!
+ * \brief Global extension state callback function
+ */
+static int exten_state_publisher_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
+{
+	struct ao2_iterator publisher_iter;
+	struct exten_state_publisher *publisher;
+
+	publisher_iter = ao2_iterator_init(publishers, 0);
+	for (; (publisher = ao2_iterator_next(&publisher_iter)); ao2_ref(publisher, -1)) {
+		if ((publisher->context_filter && regexec(&publisher->context_regex, context, 0, NULL, 0)) ||
+		    (publisher->exten_filter && regexec(&publisher->exten_regex, exten, 0, NULL, 0))) {
+			continue;
+		}
+		/* This is a placeholder for additional code to come */
+	}
+	ao2_iterator_destroy(&publisher_iter);
+
+	return 0;
+}
+
+/*!
+ * \brief Hashing function for extension state publisher
+ */
+static int exten_state_publisher_hash(const void *obj, const int flags)
+{
+	const struct exten_state_publisher *object;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		object = obj;
+		key = object->name;
+		break;
+	default:
+		ast_assert(0);
+		return 0;
+	}
+	return ast_str_hash(key);
+}
+
+/*!
+ * \brief Comparator function for extension state publisher
+ */
+static int exten_state_publisher_cmp(void *obj, void *arg, int flags)
+{
+	const struct exten_state_publisher *object_left = obj;
+	const struct exten_state_publisher *object_right = arg;
+	const char *right_key = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = object_right->name;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(object_left->name, right_key);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		/* Not supported by container. */
+		ast_assert(0);
+		return 0;
+	default:
+		cmp = 0;
+		break;
+	}
+	if (cmp) {
+		return 0;
+	}
+	return CMP_MATCH;
+}
+
+/*!
+ * \brief Destructor for extension state publisher
+ */
+static void exten_state_publisher_destroy(void *obj)
+{
+	struct exten_state_publisher *publisher = obj;
+
+	if (publisher->context_filter) {
+		regfree(&publisher->context_regex);
+	}
+
+	if (publisher->exten_filter) {
+		regfree(&publisher->exten_regex);
+	}
+
+	ao2_cleanup(publisher->client);
+}
+
+static int build_regex(regex_t *regex, const char *text)
+{
+	int res;
+
+	if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
+		size_t len = regerror(res, regex, NULL, 0);
+		char buf[len];
+		regerror(res, regex, buf, len);
+		ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int publisher_start(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
+{
+	struct exten_state_publisher *publisher;
+	size_t name_size;
+	size_t body_type_size;
+	size_t body_subtype_size;
+	char *body_subtype;
+	const char *body_full;
+	const char *body_type;
+	const char *name;
+	const char *context;
+	const char *exten;
+
+	name = ast_sorcery_object_get_id(configuration);
+
+	body_full = ast_sorcery_object_get_extended(configuration, "body");
+	if (ast_strlen_zero(body_full)) {
+		ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Body not set\n",
+			name);
+		return -1;
+	}
+
+	body_subtype = ast_strdupa(body_full);
+	body_type = strsep(&body_subtype, "/");
+	if (ast_strlen_zero(body_type) || ast_strlen_zero(body_subtype)) {
+		ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Body '%s' missing type or subtype\n",
+			name, body_full);
+		return -1;
+	}
+
+	name_size = strlen(name) + 1;
+	body_type_size = strlen(body_type) + 1;
+	body_subtype_size = strlen(body_subtype) + 1;
+
+	publisher = ao2_alloc_options(
+		sizeof(*publisher) + name_size + body_type_size + body_subtype_size,
+		exten_state_publisher_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!publisher) {
+		return -1;
+	}
+
+	ast_copy_string(publisher->name, name, name_size);
+	publisher->body_type = publisher->name + name_size;
+	ast_copy_string(publisher->body_type, body_type, body_type_size);
+	publisher->body_subtype = publisher->body_type + body_type_size;
+	ast_copy_string(publisher->body_subtype, body_subtype, body_subtype_size);
+
+	context = ast_sorcery_object_get_extended(configuration, "context");
+	if (!ast_strlen_zero(context)) {
+		if (build_regex(&publisher->context_regex, context)) {
+			ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Could not build context filter '%s'\n",
+				name, context);
+			ao2_ref(publisher, -1);
+			return -1;
+		}
+
+		publisher->context_filter = 1;
+	}
+
+	exten = ast_sorcery_object_get_extended(configuration, "exten");
+	if (!ast_strlen_zero(exten)) {
+		if (build_regex(&publisher->exten_regex, exten)) {
+			ast_log(LOG_ERROR, "Outbound extension state publisher '%s': Could not build exten filter '%s'\n",
+				name, exten);
+			ao2_ref(publisher, -1);
+			return -1;
+		}
+
+		publisher->exten_filter = 1;
+	}
+
+	publisher->client = ao2_bump(client);
+
+	ao2_lock(publishers);
+	if (!ao2_container_count(publishers)) {
+		ast_extension_state_add(NULL, NULL, exten_state_publisher_state_cb, NULL);
+	}
+	ao2_link_flags(publishers, publisher, OBJ_NOLOCK);
+	ao2_unlock(publishers);
+
+	ao2_ref(publisher, -1);
+
+	return 0;
+}
+
+static int publisher_stop(struct ast_sip_outbound_publish_client *client)
+{
+	ao2_find(publishers, ast_sorcery_object_get_id(client), OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
+	return 0;
+}
+
+static int unload_module(void)
+{
+	ast_sip_unregister_event_publisher_handler(&dialog_publisher);
+	ast_sip_unregister_subscription_handler(&dialog_handler);
+	ast_sip_unregister_event_publisher_handler(&presence_publisher);
+	ast_sip_unregister_subscription_handler(&presence_handler);
+
+	ast_extension_state_del(0, exten_state_publisher_state_cb);
+	ao2_cleanup(publishers);
+	publishers = NULL;
+
+	return 0;
+}
+
 static int load_module(void)
 {
-	CHECK_PJSIP_MODULE_LOADED();
+	CHECK_PJSIP_PUBSUB_MODULE_LOADED();
+
+	if (!ast_module_check("res_pjsip_outbound_publish.so")) {
+		ast_log(LOG_WARNING, "This module requires the 'res_pjsip_outbound_publish.so' module to be loaded\n");
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	publishers = ao2_container_alloc(PUBLISHER_BUCKETS, exten_state_publisher_hash,
+		exten_state_publisher_cmp);
+	if (!publishers) {
+		ast_log(LOG_WARNING, "Unable to create container to store extension state publishers\n");
+		return AST_MODULE_LOAD_DECLINE;
+	}
 
 	if (ast_sip_register_subscription_handler(&presence_handler)) {
 		ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
 			presence_handler.event_name);
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (ast_sip_register_event_publisher_handler(&presence_publisher)) {
+		ast_log(LOG_WARNING, "Unable to register presence publisher %s\n",
+			presence_publisher.event_name);
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
 	if (ast_sip_register_subscription_handler(&dialog_handler)) {
 		ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
 			dialog_handler.event_name);
-		ast_sip_unregister_subscription_handler(&presence_handler);
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (ast_sip_register_event_publisher_handler(&dialog_publisher)) {
+		ast_log(LOG_WARNING, "Unable to register presence publisher %s\n",
+			dialog_publisher.event_name);
+		unload_module();
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
 	return AST_MODULE_LOAD_SUCCESS;
-}
-
-static int unload_module(void)
-{
-	ast_sip_unregister_subscription_handler(&dialog_handler);
-	ast_sip_unregister_subscription_handler(&presence_handler);
-	return 0;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State Notifications",

-- 
To view, visit https://gerrit.asterisk.org/2616
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia7e630136dfc355073c1cadff8ad394a08523d78
Gerrit-PatchSet: 6
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>



More information about the asterisk-commits mailing list