[asterisk-commits] kharwell: branch kharwell/pimp_sip_state r387263 - /team/kharwell/pimp_sip_st...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed May 1 17:09:18 CDT 2013


Author: kharwell
Date: Wed May  1 17:09:17 2013
New Revision: 387263

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=387263
Log:
initial progress on extension state - won't compile

Added:
    team/kharwell/pimp_sip_state/res/res_sip_exten_state.c   (with props)

Added: team/kharwell/pimp_sip_state/res/res_sip_exten_state.c
URL: http://svnview.digium.com/svn/asterisk/team/kharwell/pimp_sip_state/res/res_sip_exten_state.c?view=auto&rev=387263
==============================================================================
--- team/kharwell/pimp_sip_state/res/res_sip_exten_state.c (added)
+++ team/kharwell/pimp_sip_state/res/res_sip_exten_state.c Wed May  1 17:09:17 2013
@@ -1,0 +1,561 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Kevin Harwell <kharwell at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+	<depend>pjproject</depend>
+	<depend>res_sip</depend>
+	<depend>res_sip_pubsub</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_simple.h>
+#include <pjlib.h>
+
+#include "asterisk/res_sip.h"
+#include "asterisk/res_sip_pubsub.h"
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/app.h"
+
+/*!
+ * \internal
+ * \brief Extension state notifier.
+ */
+struct exten_state_notifier {
+	/*! The name of the notifier */
+	const char *name;
+	/*! The name of the event this notifier registers for */
+	const char *event_name;
+	/*! The types of body this notifier accepts */
+	const char *accept[AST_SIP_MAX_ACCEPT];
+};
+
+AST_RWLIST_HEAD_STATIC(notifiers, exten_state_notifer);
+
+static int register_exten_state_notifier(struct exten_state_notifier *obj)
+{
+	SCOPED_LOCK(lock, &notifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	AST_LIST_INSERT_HEAD(&notifiers, obj);
+	ast_module_ref(ast_module_info->self);
+	return 0;
+}
+
+static void unregister_exten_state_notifier(struct exten_state_notifier *obj)
+{
+	struct exten_state_notifier *i;
+	SCOPED_LOCK(lock, &notifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&notifers, i, next) {
+		if (i == obj) {
+			AST_RWLIST_REMOVE_CURRENT(next);
+			ast_module_unref(ast_module_info->self);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+}
+
+static int has_accept(const char accept[], const char *value);
+
+static struct exten_state_notifier *notifier_by_accept(const char *accept)
+{
+	struct exten_state_notifier *res = NULL;
+	struct exten_state_notifier *i;
+	SCOPED_LOCK(lock, &notifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&notifers, i, next) {
+		if (i->accept, accept) {
+			res = i;
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+	return res;
+}
+
+static int sub_handler_hash(const void *obj, int flags)
+{
+	const struct ast_sip_subscription_handler *handler = obj;
+	
+	return ast_str_hash(handler->event_name);
+}
+
+static int sub_handler_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_sip_subscription_handler *handler1 = obj;
+	struct ast_sip_subscription_handler *handler2 = arg;
+
+	return !strcmp(handler1->event_name, handler2->event_name);
+}
+
+#define SUB_HANDLER_BUCKETS 5
+
+static struct ao2_container *sub_handlers;
+
+/*!
+ * \brief A subscription for extension state
+ *
+ * This structure acts as the owner for the underlying SIP subscription. It
+ * also keeps a pointer to an associated "notifier" so when a state changes
+ * a notify data creator is quickly accessible.
+ */
+struct state_sub {
+	/*! The SIP subscription */
+	struct ast_sip_subscription *sip_sub;
+	/*! The notify data creator */
+	struct exten_state_notifier *notifier;
+};
+
+static void state_sub_destructor(void *obj)
+{
+	struct exten_state_sub *sub = obj;
+	ao2_cleanup(sub->sip_sub);
+}
+
+/*!
+ * \internal
+ * \brief Find a subscription handler based on a given event type.
+ *
+ * Attempts to locate an event header in the given request data. If found
+ * tries to find a subscription handler for the event type.
+ */
+static struct sub_handler *sub_handler_by_req(pjsip_rx_data *rdata)
+{
+	char type[25];
+	pjsip_str_t event_name = { "Event", 5 }
+	pjsip_event_hdr *hdr = (pjsip_event_hdr*)
+		pjsip_msg_find_hdr_by_name(rdata->msg, &event_name, NULL);
+
+	if (!hdr) {
+		ast_log(LOG_WARNING, "No Event header found in subscribe\n");
+		return NULL;
+	}
+
+	ast_copy_pj_str(type, &hdr->event_type, sizeof(type));
+
+	return (struct sub_handler*)ao2_find(sub_handlers, 
+					     type, OBJ_KEY | OBJ_NOLOCK);
+}
+
+/*!
+ * \internal
+ * \brief Find a notifier for based on a given message accept type.
+ *
+ * Attempts to locate accept headers in the given request data. If found
+ * tries to find a notifier for the accept type.
+ */
+static struct exten_state_notifer *notifier_by_req(pjsip_rx_data *rdata)
+{
+	int i, j;
+	char type[50];
+	pjsip_accept_hdr *hdr = (pjsip_accept_hdr*)
+		pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+	struct exten_state_notifer *res;
+
+	if (!hdr) {
+		ast_log(LOG_WARNING, "No Accept header found in subscribe\n");
+		return NULL;
+	}
+
+	for (i = 0; i < hdr->count; ++i) {
+		ast_copy_pj_str(type, &hdr->values[i], sizeof(type));
+		if ((res = notifier_by_accept(type))) {
+			return res;
+		}
+	}
+	return NULL;
+}
+
+/*!
+ * \internal
+ * \brief Allocates an exten_state_sub object.
+ *
+ * Creates the underlying SIP subscription for the given request. First makes
+ * sure that there are registered handler and notifier objects available.
+ */
+static struct exten_state_sub *state_sub_alloc(struct ast_sip_endpoint *endpoint,
+		enum ast_sip_subscription_role role, pjsip_rx_data *rdata)
+{
+	struct sub_handler *handler;
+	struct exten_state_sub *sub;
+
+	if (!(sub = ao2_alloc(sizeof(*sub), state_sub_destructor))) {
+		return NULL;
+	}
+
+	if (!(sub_handler = sub_handler_by_req(rdata))) {
+		ast_log(LOG_WARNING, "Handler not found for subscription event\n");
+		ao2_cleanup(sub);
+		return NULL;
+	}
+
+	if (!(sub->notifier = notifier_by_req(rdata))) {
+		ast_log(LOG_WARNING, "Unable to locate subscription handler notifier\n");
+		ao2_cleanup(sub);
+		return NULL;
+	}
+
+	if (!(sub->sip_sub = ast_sip_create_subscription(sub_handler, role, endpoint, rdata))) {
+		ast_log(LOG_WARNING, "Unable to create SIP subscription for endpoint %s\n",
+			ast_sorcery_object_get_id(endpoint)->id);
+		ao2_cleanup(sub);
+		return NULL;
+	}
+
+	return sub;
+}
+
+static void send_notify(struct state_sub *sub, pjsip_evsub_state state, const char *reason)
+{
+	const pj_str_t *reason_str_ptr = NULL;
+
+	static pjsip_media_type mwi_type = {
+		.type = { "application", 11 },
+		.subtype = { "simple-message-summary", 22 },
+	};
+
+	RAII_VAR(struct ast_str *, body, ast_str_create(64), ast_free_ptr);
+	pjsip_tx_data *tdata;
+	pj_str_t reason_str;
+	pj_str_t pj_body;
+
+	ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter);
+
+	if (reason) {
+		pj_cstr(&reason_str, reason);
+		reason_str_ptr = &reason_str;
+	}
+	ast_str_append(&body, 0, "Messages-Waiting: %s\r\n", counter.new_msgs ? "yes" : "no");
+	ast_str_append(&body, 0, "Voice-Message: %d/%d (0/0)\r\n", counter.new_msgs, counter.old_msgs);
+	pj_cstr(&pj_body, ast_str_buffer(body));
+
+	if (pjsip_evsub_notify(ast_sip_subscription_get_evsub(sub->sip_sub), state,
+			       NULL, reason ? pj_str(reason) : NULL, &tdata) != PJ_SUCCESS) {
+		ast_log(LOG_WARNING, "Unable to create NOTIFY request to %s.\n", sub->id);
+		return;
+	}
+
+	if (ast_sip_subscription_send_request(sub->sip_sub, tdata) != PJ_SUCCESS) {
+		ast_log(LOG_WARNING, "Unable to send MWI NOTIFY request to %s\n", sub->id);
+		pjsip_tx_data_dec_ref(tdata);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Callback for exten/device state changes.
+ *
+ * Upon state change, unless it has been deactivated or removed then send the
+ * appropriate notification to the subscriber.
+ */
+static int state_changed(char *context, char *exten, 
+			 struct ast_state_cb_info *info, void *data)
+{
+	struct state_notify_data notify_data = {
+		.state = info->exten_state,
+		.device_state_info = info->device_state_info,
+		.presence_state = info->presence_state,
+		.presence_subtype = info->presence_subtype,
+		.presence_message = info->presence_message,
+	};
+
+	switch (notify_data->state) {
+	case AST_EXTENSION_DEACTIVATED:
+	case AST_EXTENSION_REMOVED:
+		ast_verb(2, "Watcher for hint %s %s\n", exten, data->state
+			 == AST_EXTENSION_REMOVED ? "removed" : "deactivated");
+		break;
+	default:
+
+	}
+
+	return extensionstate_update(context, exten, &notify_data, p, FALSE);
+}
+
+/*!
+ * \internal
+ * \brief Callback for exten/device state destruction.
+ */
+static void state_destroyed(int id, void *data)
+{
+	struct state_sub *sub = data;
+	ao2_cleanup(sub);
+}
+
+static void subscription_shutdown(struct ast_sip_subscription *sub)
+{
+	/* nothing to do? */
+}
+
+static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint,
+						  pjsip_rx_data *rdata)
+{
+	RAII_VAR(struct exten_subscription *, sub, NULL, ao2_cleanup);
+	pjsip_uri *uri = rdata->msg_info.msg->line.req.uri;
+	pjsip_sip_uri sip_uri = pjsip_uri_get_uri(ruri);
+	char exten[AST_MAX_EXTENSION];
+	char hint[AST_MAX_EXTENSION];
+
+	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+		ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n");
+		return NULL;
+	}
+
+	ast_copy_pj_str(exten, &sip_uri->user, sizeof(exten));
+
+	if (!ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, endpoint->context, exten)) {
+		ast_log(LOG_WARNING, "Hint does not exists for extension %s\n", exten);
+		return NULL:
+	}
+
+	if (!(sub = state_sub_alloc(endpoint, AST_SIP_NOTIFIER, rdata))) {
+		return NULL;
+	}
+
+	if (ast_extension_state_add_destroy(endpoint->context, exten,
+					    state_changed, state_destroyed, sub)) {
+		ast_log(LOG_WARNING, "Unable to subscribe to hint in context % extension %s\n",
+			endpoint->context, exten);
+		ao2_cleanup(sub);
+		return NULL;
+	}
+
+	pjsip_evsub_accept(ast_sip_subscription_get_evsub(sub->sip_sub),
+			   rdata, 200, NULL);
+	send_mwi_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, NULL);
+
+	return sub->sip_sub;
+}
+
+static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
+			struct ast_sip_subscription_response_data *response_data)
+{
+}
+
+static void subscription_timeout(struct ast_sip_subscription *sub)
+{
+}
+
+static void subscription_terminated(struct ast_sip_subscription *sub,
+				    pjsip_rx_data *rdata)
+{
+}
+
+static void notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata)
+{
+	/* nothing to do? */
+}
+
+static void notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
+			   struct ast_sip_subscription_response_data *response_data)
+{
+	/* nothing to do? */
+}
+
+static int refresh_subcription(struct ast_sip_subscription *sub)
+{
+	/* nothing to do? */
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Checks to see if the given value is in the given accept array.
+ */
+static int has_accept(const char accept[], const char *value)
+{
+	int i;
+	/* search all items or until we find a null/empty element */
+	for (i = 0; i < sizeof(accept) ||
+		     !ast_strlen_zero(accept[i]); ++i) {
+
+		if (!strcmp(accept[i], value)) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Assign items from 'src' to 'dest' if 'dest' does not
+ *        already contain the item.
+ */
+static void assign_accept(char* dest[], const char* src[])
+{
+	int i = 0, j;
+
+	/* find the first empty element in the array */
+	while (dest[i]) ++i;
+
+	/* for every item in src, if it is not already in dest
+	   then add it to it */
+	for (j = 0; j < sizeof(src) && i < sizeof(dest); ++j) {
+		if (!has_accept(dest, src[j])) {
+			dest[i++] = src[j];
+		}
+	}
+}
+
+/*!
+ * \internal
+ * \brief Create a subscription handler.
+ *
+ * Creates a subscription handler that can be registered with the pub/sub
+ * framework for the given event_name and accept values.
+ */
+static struct ast_sip_subscription_handler *create_sub_handler(const char *event_name,
+							       const char accept[])
+{
+	struct ast_sip_subscription_handler *handler = 
+		ao2_alloc(sizeof(*handler), NULL);
+	
+	if (!handler) {
+		return NULL;
+	}
+
+	handler->event_name = event_name;
+	assign_accept(handler->accept, accept);
+
+	handler->subscription_shutdown = subscription_shutdown;
+	handler->new_subscribe = new_subscribe;
+	handler->resubscribe = resubscribe;
+	handler->subscription_timeout = subscription_timeout;
+	handler->subscription_terminated = subscription_terminated;
+	handler->notify_response = notify_response;
+	handler->notify_request = notify_request;
+	handler->refresh_subscription = refresh_subscription;
+
+	return handler;
+}
+
+/*!
+ * \internal
+ * \brief Create a collection of subscription handlers.
+ *
+ * Currently pjsip only allows one event_name to be registered for a given
+ * list of body type/application (accept) values. This list of values 
+ * cannot be modified once a particular event_name has been registered.
+ * This means all accept values must be specified before registering a
+ * subcription handler for an event_name with the pub/sub framework.
+ *
+ * For every registered notifier create, or re-use, a subscription handler
+ * based upon the event_name. If a handler does not exist for a particular
+ * event_name one will be created adding all accept values from a notifier.
+ * If a handler already exists for an event_name then any extra accept
+ * values that have not been assigned to that handler will be.
+ */
+static void create_sub_handlers()
+{
+	struct exten_state_notifier *i;
+	SCOPED_LOCK(lock, &notifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&notifers, i, next) {
+		struct ast_sip_subscription_handler *handler =
+			ao2_find(sub_handlers, n->event_name, OBJ_NOLOCK | OBJ_KEY);
+
+		if (handler) {
+			/* if handler exists just assign those accept values to
+			   it from the notifier that have not already been set */
+			assign_accept(handler->accept, i->accept);
+			continue;
+		}
+
+		/* if handler does not exist yet, create it and add to 
+		   container - note accept values assigned in create */
+		if ((handler = create_sub_handler(event_name))) {
+			ao2_link(sub_handlers, handler);
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+}
+
+/*!
+ * \internal
+ * \brief Register a subscription handlers with the pub/sub framework.
+ */
+static int register_sub_handlers(void *obj, void *arg, int flags)
+{
+	struct ast_sip_subscription_handler *handler = obj;
+	int *count = arg;
+
+	if (ast_sip_register_subscription_handler(obj)) {
+		ast_log(LOG_WARNING, "Unable to register subscription handler %s",
+			handler->event_name);
+	} else {
+		/* if at least one handler registers we will
+		   declare load success*/
+		(*count)++;
+	}
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Unregister a subscription handler with the pub/sub framework.
+ */
+static void unregister_sub_handlers(void *obj, void *arg, int flags)
+{
+	ast_sip_unregister_subscription_handler(obj);
+}
+
+static int load_module(void)
+{
+	int count = 0;
+
+	if (!(sub_handlers = ao2_container_alloc(SUB_HANDLER_BUCKETS,
+						 sub_handler_hash, sub_handler_cmp))) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+	/* register notifiers - for now they have to be specifically added here
+	   because of a limitation in pjsip. if pjsip ever gets modified these
+	   can be moved to their respective module files */
+	/* register_exten_state_notifier(obj) */
+
+	/* create the subscription handlers based upon the notifiers */
+	create_sub_handlers();
+
+	/* register each subscription handler declaring success if
+	   at least one registers */
+	ao2_callback(sub_handlers, OBJ_NODATA | OBJ_NOLOCK,
+		     register_sub_handlers, &count);
+
+	return count ? AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_FAILURE;
+}
+
+static int unload_module(void)
+{
+	ao2_callback(sub_handlers, OBJ_NODATA | OBJ_NOLOCK,
+		     unregister_sub_handlers, NULL);
+
+	/* unregister notifiers here, again due to pjsip limitation */
+	/* unregister_exten_state_notifier(obj) */
+
+	ao2_cleanup(sub_handlers);
+
+	return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Extension State Notifications",
+		.load = load_module,
+		.unload = unload_module,
+		.load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);

Propchange: team/kharwell/pimp_sip_state/res/res_sip_exten_state.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/kharwell/pimp_sip_state/res/res_sip_exten_state.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Rev URL

Propchange: team/kharwell/pimp_sip_state/res/res_sip_exten_state.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the asterisk-commits mailing list