[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, ¬ifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+ AST_LIST_INSERT_HEAD(¬ifiers, 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, ¬ifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifers, 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, ¬ifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifers, 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, ¬ify_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, ¬ifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifers, 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