[asterisk-commits] kharwell: branch kharwell/pimp_sip_state r387543 - /team/kharwell/pimp_sip_st...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu May 2 17:59:15 CDT 2013
Author: kharwell
Date: Thu May 2 17:59:14 2013
New Revision: 387543
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=387543
Log:
compiling now yay. hopefully the bulk of the code is in, but haven't tested yet.
Modified:
team/kharwell/pimp_sip_state/res/res_sip_exten_state.c
Modified: 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=diff&rev=387543&r1=387542&r2=387543
==============================================================================
--- team/kharwell/pimp_sip_state/res/res_sip_exten_state.c (original)
+++ team/kharwell/pimp_sip_state/res/res_sip_exten_state.c Thu May 2 17:59:14 2013
@@ -36,33 +36,36 @@
#include "asterisk/astobj2.h"
#include "asterisk/sorcery.h"
#include "asterisk/app.h"
+#include "asterisk/pbx.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 type of body this notifier accepts */
const char *accept;
/*!
- * \brief Called before sending the notify request.
+ * \brief Create the body text of a NOTIFY request.
*
- * Allows the notifier to set data on the outgoing request.
+ * Implementors use this to create body information within the given
+ * ast_str. That information is then added to the NOTIFY request.
*/
- void (*set_tdata)(pjsip_tx_data *tdata);
+ void (*create_body)(struct ao2_container *info, struct ast_str *body_text);
+
+ /*! Next item in the list */
+ AST_LIST_ENTRY(exten_state_notifier) next;
};
-AST_RWLIST_HEAD_STATIC(notifiers, exten_state_notifer);
+AST_RWLIST_HEAD_STATIC(notifiers, exten_state_notifier);
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_LIST_INSERT_TAIL(¬ifiers, obj, next);
ast_module_ref(ast_module_info->self);
return 0;
}
@@ -71,7 +74,7 @@
{
struct exten_state_notifier *i;
SCOPED_LOCK(lock, ¬ifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifers, i, next) {
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifiers, i, next) {
if (i == obj) {
AST_RWLIST_REMOVE_CURRENT(next);
ast_module_unref(ast_module_info->self);
@@ -81,12 +84,16 @@
AST_RWLIST_TRAVERSE_SAFE_END;
}
+/*!
+ * \internal
+ * \brief Find a notifier based on the given accept type.
+ */
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) {
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifiers, i, next) {
if (!strcmp(i->accept, accept)) {
res = i;
break;
@@ -94,6 +101,35 @@
}
AST_RWLIST_TRAVERSE_SAFE_END;
return res;
+}
+
+/*!
+ * \internal
+ * \brief Find a notifier 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_notifier *notifier_by_req(pjsip_rx_data *rdata)
+{
+ int i;
+ char type[50];
+ pjsip_accept_hdr *hdr = (pjsip_accept_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
+ struct exten_state_notifier *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;
}
static int sub_handler_hash(const void *obj, int flags)
@@ -127,11 +163,19 @@
struct ast_sip_subscription *sip_sub;
/*! The notify data creator */
struct exten_state_notifier *notifier;
+ /*! Context in which subscription looks for updates */
+ const char *context;
+ /*! Extension within the context to receive updates from */
+ char exten[AST_MAX_EXTENSION];
+ /*! Last state information set during state change callback */
+ struct ao2_container *state_info;
+ /*! State when sending an serialized notify */
+ pjsip_evsub_state state;
};
static void state_sub_destructor(void *obj)
{
- struct exten_state_sub *sub = obj;
+ struct state_sub *sub = obj;
ao2_cleanup(sub->sip_sub);
}
@@ -142,12 +186,12 @@
* 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)
+static struct ast_sip_subscription_handler *sub_handler_by_req(pjsip_rx_data *rdata)
{
char type[25];
- pjsip_str_t event_name = { "Event", 5 }
+ pj_str_t event_name = { "Event", 5 };
pjsip_event_hdr *hdr = (pjsip_event_hdr*)
- pjsip_msg_find_hdr_by_name(rdata->msg, &event_name, NULL);
+ pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &event_name, NULL);
if (!hdr) {
ast_log(LOG_WARNING, "No Event header found in subscribe\n");
@@ -156,57 +200,28 @@
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.
+ return (struct ast_sip_subscription_handler*)
+ ao2_find(sub_handlers, type, OBJ_KEY | OBJ_NOLOCK);
+}
+
+/*!
+ * \internal
+ * \brief Allocates an 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,
+static struct 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;
+ struct ast_sip_subscription_handler *handler;
+ struct state_sub *sub;
if (!(sub = ao2_alloc(sizeof(*sub), state_sub_destructor))) {
return NULL;
}
- if (!(sub_handler = sub_handler_by_req(rdata))) {
+ if (!(handler = sub_handler_by_req(rdata))) {
ast_log(LOG_WARNING, "Handler not found for subscription event\n");
ao2_cleanup(sub);
return NULL;
@@ -218,9 +233,9 @@
return NULL;
}
- if (!(sub->sip_sub = ast_sip_create_subscription(sub_handler, role, endpoint, rdata))) {
+ if (!(sub->sip_sub = ast_sip_create_subscription(handler, role, endpoint, rdata))) {
ast_log(LOG_WARNING, "Unable to create SIP subscription for endpoint %s\n",
- ast_sorcery_object_get_id(endpoint)->id);
+ ast_sorcery_object_get_id(endpoint));
ao2_cleanup(sub);
return NULL;
}
@@ -228,149 +243,229 @@
return sub;
}
-static void send_notify(struct state_sub *sub, pjsip_evsub_state state, const char *reason)
-{
- pjsip_media_type body_type;
+/*!
+ * \internal
+ * \brief Create and send a NOTIFY request to the subscriber.
+ */
+static void create_send_notify(struct state_sub *sub, pjsip_evsub_state state,
+ const char *reason, struct ao2_container *info)
+{
+ RAII_VAR(struct ast_str *, body_text, ast_str_create(512), ast_free_ptr);
+ pj_str_t reason_str;
+ const pj_str_t *reason_str_ptr = NULL;
+ pjsip_tx_data *tdata;
+ struct ast_sip_body body;
char type[50];
- char *subtype;
-
- ast_copy_string(type, sub->notifier->accept, sizeof(type));
- if ((subtype = strchr(type, '/'))) {
- *subtype++ = '\0';
- body_type.subtype = pj_str_t(subtype);
- }
-
- body_type.type = pj_str_t(type);
-
- 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 (!(body.subtype = strchr(sub->notifier->accept, '/'))) {
+ ast_log(LOG_WARNING, "Subtype not specified for notifier\n");
+ return;
+ }
+
+ ast_copy_string(type, sub->notifier->accept,
+ body.subtype - sub->notifier->accept);
+ body.type = type;
+ ++body.subtype;
+
+ if (!sub->notifier->create_body) {
+ ast_log(LOG_WARNING, "Body handler not specified for notifier\n");
+ return;
+ }
+
+ sub->notifier->create_body(info, body_text);
+ body.body_text = ast_str_buffer(body_text);
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 (sub->notifier->set_data) {
- sub->notifier->set_data(tdata);
+ NULL, reason_str_ptr, &tdata) != PJ_SUCCESS) {
+ ast_log(LOG_WARNING, "Unable to create NOTIFY request\n");
+ return;
+ }
+
+ if (ast_sip_add_body(tdata, &body)) {
+ ast_log(LOG_WARNING, "Unable to add body to NOTIFY request\n");
+ pjsip_tx_data_dec_ref(tdata);
+ 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);
+ ast_log(LOG_WARNING, "Unable to send NOTIFY request\n");
pjsip_tx_data_dec_ref(tdata);
}
}
/*!
* \internal
+ * \brief Get device state information and send notification to the subscriber.
+ */
+static void send_notify(struct state_sub *ssub, pjsip_evsub_state state, const char *reason)
+{
+ RAII_VAR(struct ao2_container*, info, NULL, ao2_cleanup);
+
+ if (ast_extension_state_extended(NULL, ssub->context, ssub->exten, &info)) {
+ ast_log(LOG_WARNING, "Unable to get device info for extension %s\n", ssub->exten);
+ return;
+ }
+
+ create_send_notify(ssub, state, reason, info);
+}
+
+static int serialized_notify(void *data)
+{
+ struct state_sub *ssub = data;
+ create_send_notify(ssub, ssub->state, NULL, ssub->state_info);
+ ao2_ref(ssub, -1);
+ return 0;
+}
+
+/*!
+ * \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.
+ * Upon state change, 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
+ struct state_sub *ssub = data;
+
+ if ((info->exten_state == AST_EXTENSION_DEACTIVATED) ||
+ (info->exten_state == AST_EXTENSION_REMOVED)) {
+ ssub->state = PJSIP_EVSUB_STATE_TERMINATED;
+ ast_log(LOG_NOTICE, "Watcher for hint %s %s\n", exten, info->exten_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);
+ }
+
+ ao2_ref(ssub, +1);
+ ssub->state = PJSIP_EVSUB_STATE_ACTIVE;
+ ssub->state_info = info->device_state_info;
+ ast_sip_push_task(ast_sip_subscription_get_serializer(ssub->sip_sub),
+ serialized_notify, ssub);
+ return 0;
+}
+
+static struct ast_datastore_info ds_info = { };
+static const char ds_name[] = "exten state datastore";
+
+/*!
+ * \internal
+ * \brief Add a datastore for exten state_sub.
+ *
+ * Adds the state_sub wrapper object to a datastore so it can be retrieved
+ * later based upon its association with the ast_sip_subscription.
+ */
+static int add_datastore(struct state_sub *sub)
+{
+ RAII_VAR(struct ast_datastore *, datastore,
+ ast_sip_subscription_alloc_datastore(&ds_info, ds_name), ao2_cleanup);
+
+ if (!datastore) {
+ return -1;
+ }
+
+ datastore->data = sub;
+ ast_sip_subscription_add_datastore(sub->sip_sub, datastore);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Get the state_sub object associated with the given
+ * ast_sip_subscription in the datastore.
+ */
+static struct state_sub *from_datastore(struct ast_sip_subscription *sub)
+{
+ RAII_VAR(struct ast_datastore *, datastore,
+ ast_sip_subscription_get_datastore(sub, ds_name), ao2_cleanup);
+
+ return datastore ? datastore->data : NULL;
}
static void subscription_shutdown(struct ast_sip_subscription *sub)
{
- /* nothing to do? */
+ struct state_sub *ssub = from_datastore(sub);
+
+ if (ssub) {
+ ao2_cleanup(ssub);
+ }
}
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];
+ pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri);
+ struct state_sub *ssub;
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),
+ if (!(ssub = state_sub_alloc(endpoint, AST_SIP_NOTIFIER, rdata))) {
+ return NULL;
+ }
+
+ ssub->context = endpoint->context;
+ ast_copy_pj_str(ssub->exten, &sip_uri->user, sizeof(ssub->exten));
+
+ if (ast_extension_state_add(ssub->context, ssub->exten, state_changed, ssub)) {
+ ast_log(LOG_WARNING, "Unable to subscribe extension %s updates\n",
+ ssub->exten);
+ ao2_cleanup(ssub);
+ return NULL;
+ }
+
+ if (add_datastore(ssub)) {
+ ast_log(LOG_WARNING, "Unable to add to subscription datastore.\n");
+ ao2_cleanup(ssub);
+ return NULL;
+ }
+
+ pjsip_evsub_accept(ast_sip_subscription_get_evsub(ssub->sip_sub),
rdata, 200, NULL);
- send_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, NULL);
-
- return sub->sip_sub;
+
+ send_notify(ssub, PJSIP_EVSUB_STATE_ACTIVE, NULL);
+ return ssub->sip_sub;
}
static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
struct ast_sip_subscription_response_data *response_data)
{
+ struct state_sub *ssub = from_datastore(sub);
+
+ if (ssub) {
+ send_notify(ssub, PJSIP_EVSUB_STATE_ACTIVE, NULL);
+ }
}
static void subscription_timeout(struct ast_sip_subscription *sub)
{
+ struct state_sub *ssub = from_datastore(sub);
+
+ if (!ssub) {
+ return;
+ }
+
+ ast_log(LOG_NOTICE, "Subscription for has timed out.\n");
+ send_notify(ssub, PJSIP_EVSUB_STATE_TERMINATED, "timeout");
}
static void subscription_terminated(struct ast_sip_subscription *sub,
pjsip_rx_data *rdata)
{
+ struct state_sub *ssub = from_datastore(sub);
+
+ if (!ssub) {
+ return;
+ }
+
+ ast_log(LOG_NOTICE, "Subscription has been terminated\n");
+ send_notify(ssub, PJSIP_EVSUB_STATE_TERMINATED, NULL);
}
static void notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata)
@@ -384,7 +479,7 @@
/* nothing to do? */
}
-static int refresh_subcription(struct ast_sip_subscription *sub)
+static int refresh_subscription(struct ast_sip_subscription *sub)
{
/* nothing to do? */
return 0;
@@ -394,7 +489,7 @@
* \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)
+static int has_accept(const char *accept[], const char *value)
{
int i;
/* search all items or until we find a null/empty element */
@@ -413,7 +508,7 @@
* \brief Add given accept to 'dest' if 'dest' does not already contain
* the accept type.
*/
-static void add_accept(char* dest[], const char* accept)
+static void add_accept(const char* dest[], const char* accept)
{
int i = 0;
@@ -480,13 +575,13 @@
* If a handler already exists for an event_name then if the accept value
* has not been added to that handler it will be.
*/
-static void create_sub_handlers()
+static void create_sub_handlers(void)
{
struct exten_state_notifier *i;
SCOPED_LOCK(lock, ¬ifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifers, i, next) {
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(¬ifiers, i, next) {
struct ast_sip_subscription_handler *handler =
- ao2_find(sub_handlers, n->event_name, OBJ_NOLOCK | OBJ_KEY);
+ ao2_find(sub_handlers, i->event_name, OBJ_NOLOCK | OBJ_KEY);
if (handler) {
/* if handler exists just assign those accept values to
@@ -497,7 +592,7 @@
/* 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))) {
+ if ((handler = create_sub_handler(i->event_name, i->accept))) {
ao2_link(sub_handlers, handler);
}
}
@@ -528,9 +623,10 @@
* \internal
* \brief Unregister a subscription handler with the pub/sub framework.
*/
-static void unregister_sub_handlers(void *obj, void *arg, int flags)
+static int unregister_sub_handlers(void *obj, void *arg, int flags)
{
ast_sip_unregister_subscription_handler(obj);
+ return 0;
}
static int load_module(void)
@@ -545,7 +641,7 @@
/* 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) */
+ register_exten_state_notifier(NULL);
/* create the subscription handlers based upon the notifiers */
create_sub_handlers();
@@ -564,7 +660,7 @@
unregister_sub_handlers, NULL);
/* unregister notifiers here, again due to pjsip limitation */
- /* unregister_exten_state_notifier(obj) */
+ unregister_exten_state_notifier(NULL);
ao2_cleanup(sub_handlers);
More information about the asterisk-commits
mailing list