summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip/presence_xml.c166
-rw-r--r--res/res_pjsip_exten_state.c263
-rw-r--r--res/res_pjsip_mwi.c42
-rw-r--r--res/res_pjsip_mwi_body_generator.c112
-rw-r--r--res/res_pjsip_pidf.c398
-rw-r--r--res/res_pjsip_pidf_body_generator.c135
-rw-r--r--res/res_pjsip_pidf_eyebeam_body_supplement.c113
-rw-r--r--res/res_pjsip_pubsub.c268
-rw-r--r--res/res_pjsip_pubsub.exports.in8
-rw-r--r--res/res_pjsip_xpidf_body_generator.c177
10 files changed, 1009 insertions, 673 deletions
diff --git a/res/res_pjsip/presence_xml.c b/res/res_pjsip/presence_xml.c
new file mode 100644
index 000000000..31e06eba4
--- /dev/null
+++ b/res/res_pjsip/presence_xml.c
@@ -0,0 +1,166 @@
+/*
+ * asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Kevin Harwell <kharwell@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_pjsip</depend>
+ <depend>res_pjsip_pubsub</depend>
+ <depend>res_pjsip_exten_state</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_simple.h>
+#include <pjlib.h>
+
+#include "asterisk/module.h"
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/res_pjsip_presence_xml.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
+
+void ast_sip_sanitize_xml(const char *input, char *output, size_t len)
+{
+ char *copy = ast_strdupa(input);
+ char *break_point;
+
+ output[0] = '\0';
+
+ while ((break_point = strpbrk(copy, "<>\"&'"))) {
+ char to_escape = *break_point;
+
+ *break_point = '\0';
+ strncat(output, copy, len);
+
+ switch (to_escape) {
+ case '<':
+ strncat(output, "&lt;", len);
+ break;
+ case '>':
+ strncat(output, "&gt;", len);
+ break;
+ case '"':
+ strncat(output, "&quot;", len);
+ break;
+ case '&':
+ strncat(output, "&amp;", len);
+ break;
+ case '\'':
+ strncat(output, "&apos;", len);
+ break;
+ };
+
+ copy = break_point + 1;
+ }
+
+ /* Be sure to copy everything after the final bracket */
+ if (*copy) {
+ strncat(output, copy, len);
+ }
+}
+
+void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **pidfstate,
+ char **pidfnote, enum ast_sip_pidf_state *local_state)
+{
+ switch (state) {
+ case AST_EXTENSION_RINGING:
+ *statestring = "early";
+ *local_state = NOTIFY_INUSE;
+ *pidfstate = "busy";
+ *pidfnote = "Ringing";
+ break;
+ case AST_EXTENSION_INUSE:
+ *statestring = "confirmed";
+ *local_state = NOTIFY_INUSE;
+ *pidfstate = "busy";
+ *pidfnote = "On the phone";
+ break;
+ case AST_EXTENSION_BUSY:
+ *statestring = "confirmed";
+ *local_state = NOTIFY_CLOSED;
+ *pidfstate = "busy";
+ *pidfnote = "On the phone";
+ break;
+ case AST_EXTENSION_UNAVAILABLE:
+ *statestring = "terminated";
+ *local_state = NOTIFY_CLOSED;
+ *pidfstate = "away";
+ *pidfnote = "Unavailable";
+ break;
+ case AST_EXTENSION_ONHOLD:
+ *statestring = "confirmed";
+ *local_state = NOTIFY_CLOSED;
+ *pidfstate = "busy";
+ *pidfnote = "On hold";
+ break;
+ case AST_EXTENSION_NOT_INUSE:
+ default:
+ /* Default setting */
+ *statestring = "terminated";
+ *local_state = NOTIFY_OPEN;
+ *pidfstate = "--";
+ *pidfnote ="Ready";
+ break;
+ }
+}
+
+pj_xml_attr *ast_sip_presence_xml_create_attr(pj_pool_t *pool,
+ pj_xml_node *node, const char *name, const char *value)
+{
+ pj_xml_attr *attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr);
+
+ pj_strdup2(pool, &attr->name, name);
+ pj_strdup2(pool, &attr->value, value);
+
+ pj_xml_add_attr(node, attr);
+ return attr;
+}
+
+pj_xml_node *ast_sip_presence_xml_create_node(pj_pool_t *pool,
+ pj_xml_node *parent, const char* name)
+{
+ pj_xml_node *node = PJ_POOL_ALLOC_T(pool, pj_xml_node);
+
+ pj_list_init(&node->attr_head);
+ pj_list_init(&node->node_head);
+
+ pj_strdup2(pool, &node->name, name);
+
+ node->content.ptr = NULL;
+ node->content.slen = 0;
+
+ pj_xml_add_node(parent, node);
+ return node;
+}
+
+void ast_sip_presence_xml_find_node_attr(pj_pool_t* pool,
+ pj_xml_node *parent, const char *node_name, const char *attr_name,
+ pj_xml_node **node, pj_xml_attr **attr)
+{
+ pj_str_t name;
+
+ if (!(*node = pj_xml_find_node(parent, pj_cstr(&name, node_name)))) {
+ *node = ast_sip_presence_xml_create_node(pool, parent, node_name);
+ }
+
+ if (!(*attr = pj_xml_find_attr(*node, pj_cstr(&name, attr_name), NULL))) {
+ *attr = ast_sip_presence_xml_create_attr(pool, *node, attr_name, "");
+ }
+}
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index f312a522c..819155f00 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -31,7 +31,7 @@
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_pubsub.h"
-#include "asterisk/res_pjsip_exten_state.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/astobj2.h"
@@ -41,43 +41,6 @@
#define BODY_SIZE 1024
#define EVENT_TYPE_SIZE 50
-AST_RWLIST_HEAD_STATIC(providers, ast_sip_exten_state_provider);
-
-/*!
- * \internal
- * \brief Find a provider based on the given accept body type.
- */
-static struct ast_sip_exten_state_provider *provider_by_type(const char *type)
-{
- struct ast_sip_exten_state_provider *i;
- SCOPED_LOCK(lock, &providers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&providers, i, next) {
- if (!strcmp(i->body_type, type)) {
- return i;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
- return NULL;
-}
-
-/*!
- * \internal
- * \brief Find a provider based on the given accept body types.
- */
-static struct ast_sip_exten_state_provider *provider_by_types(const char *event_name,
- char **types, int count)
-{
- int i;
- struct ast_sip_exten_state_provider *res;
- for (i = 0; i < count; ++i) {
- if ((res = provider_by_type(types[i])) &&
- !strcmp(event_name, res->event_name)) {
- return res;
- }
- }
- return NULL;
-}
-
/*!
* \brief A subscription for extension state
*
@@ -90,12 +53,6 @@ struct exten_state_subscription {
int id;
/*! The SIP subscription */
struct ast_sip_subscription *sip_sub;
- /*! The name of the event the subscribed to */
- char event_name[EVENT_TYPE_SIZE];
- /*! The number of body types */
- int body_types_count;
- /*! The subscription body types */
- char **body_types;
/*! Context in which subscription looks for updates */
char context[AST_MAX_CONTEXT];
/*! Extension within the context to receive updates from */
@@ -104,44 +61,40 @@ struct exten_state_subscription {
enum ast_extension_states last_exten_state;
};
+#define DEFAULT_PRESENCE_BODY "application/pidf+xml"
+
+static void subscription_shutdown(struct ast_sip_subscription *sub);
+static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint,
+ pjsip_rx_data *rdata);
+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 to_ami(struct ast_sip_subscription *sub,
+ struct ast_str **buf);
+
+struct ast_sip_subscription_handler presence_handler = {
+ .event_name = "presence",
+ .accept = { DEFAULT_PRESENCE_BODY, },
+ .default_accept = DEFAULT_PRESENCE_BODY,
+ .subscription_shutdown = subscription_shutdown,
+ .new_subscribe = new_subscribe,
+ .resubscribe = resubscribe,
+ .subscription_timeout = subscription_timeout,
+ .subscription_terminated = subscription_terminated,
+ .to_ami = to_ami,
+};
+
static void exten_state_subscription_destructor(void *obj)
{
struct exten_state_subscription *sub = obj;
- int i;
-
- for (i = 0; i < sub->body_types_count; ++i) {
- ast_free(sub->body_types[i]);
- }
- ast_free(sub->body_types);
ao2_cleanup(sub->sip_sub);
}
/*!
* \internal
- * \brief Copies the body types the message wishes to subscribe to.
- */
-static void copy_body_types(pjsip_rx_data *rdata,
- struct exten_state_subscription *exten_state_sub)
-{
- int i;
- pjsip_accept_hdr *hdr = (pjsip_accept_hdr*)
- pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
-
- exten_state_sub->body_types_count = hdr->count;
- exten_state_sub->body_types = ast_malloc(hdr->count * sizeof(char*));
-
- for (i = 0; i < hdr->count; ++i) {
- exten_state_sub->body_types[i] =
- ast_malloc(hdr->values[i].slen * sizeof(char*) + 1);
-
- ast_copy_string(exten_state_sub->body_types[i],
- pj_strbuf(&hdr->values[i]), hdr->values[i].slen + 1);
- }
-}
-
-/*!
- * \internal
* \brief Initialize the last extension state to something outside
* its usual states.
*/
@@ -157,11 +110,6 @@ static void copy_body_types(pjsip_rx_data *rdata,
static struct exten_state_subscription *exten_state_subscription_alloc(
struct ast_sip_endpoint *endpoint, enum ast_sip_subscription_role role, pjsip_rx_data *rdata)
{
- static const pj_str_t event_name = { "Event", 5 };
- pjsip_event_hdr *hdr = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(
- rdata->msg_info.msg, &event_name, NULL);
-
- struct ast_sip_exten_state_provider *provider;
RAII_VAR(struct exten_state_subscription *, exten_state_sub,
ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor), ao2_cleanup);
@@ -169,19 +117,8 @@ static struct exten_state_subscription *exten_state_subscription_alloc(
return NULL;
}
- ast_copy_pj_str(exten_state_sub->event_name, &hdr->event_type,
- sizeof(exten_state_sub->event_name));
-
- copy_body_types(rdata, exten_state_sub);
- if (!(provider = provider_by_types(exten_state_sub->event_name,
- exten_state_sub->body_types,
- exten_state_sub->body_types_count))) {
- ast_log(LOG_WARNING, "Unable to locate subscription handler\n");
- return NULL;
- }
-
if (!(exten_state_sub->sip_sub = ast_sip_create_subscription(
- provider->handler, role, endpoint, rdata))) {
+ &presence_handler, role, endpoint, rdata))) {
ast_log(LOG_WARNING, "Unable to create SIP subscription for endpoint %s\n",
ast_sorcery_object_get_id(endpoint));
return NULL;
@@ -204,27 +141,13 @@ static void create_send_notify(struct exten_state_subscription *exten_state_sub,
pj_str_t reason_str;
const pj_str_t *reason_str_ptr = NULL;
pjsip_tx_data *tdata;
- pjsip_dialog *dlg;
- char local[PJSIP_MAX_URL_SIZE], remote[PJSIP_MAX_URL_SIZE];
struct ast_sip_body body;
- struct ast_sip_exten_state_provider *provider = provider_by_types(
- exten_state_sub->event_name, exten_state_sub->body_types,
- exten_state_sub->body_types_count);
-
- if (!provider) {
- ast_log(LOG_ERROR, "Unable to locate provider for subscription\n");
- return;
- }
-
- body.type = provider->type;
- body.subtype = provider->subtype;
-
- dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub);
- ast_copy_pj_str(local, &dlg->local.info_str, sizeof(local));
- ast_copy_pj_str(remote, &dlg->remote.info_str, sizeof(remote));
+ body.type = ast_sip_subscription_get_body_type(exten_state_sub->sip_sub);
+ body.subtype = ast_sip_subscription_get_body_subtype(exten_state_sub->sip_sub);
- if (provider->create_body(exten_state_data, local, remote, &body_text)) {
+ if (ast_sip_pubsub_generate_body_content(body.type, body.subtype,
+ exten_state_data, &body_text)) {
ast_log(LOG_ERROR, "Unable to create body on NOTIFY request\n");
return;
}
@@ -262,13 +185,19 @@ static void send_notify(struct exten_state_subscription *exten_state_sub, const
{
RAII_VAR(struct ao2_container*, info, NULL, ao2_cleanup);
char *subtype = NULL, *message = NULL;
-
+ pjsip_dialog *dlg;
struct ast_sip_exten_state_data exten_state_data = {
.exten = exten_state_sub->exten,
.presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
exten_state_sub->exten, &subtype, &message),
};
+ dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub);
+ ast_copy_pj_str(exten_state_data.local, &dlg->local.info_str,
+ sizeof(exten_state_data.local));
+ ast_copy_pj_str(exten_state_data.remote, &dlg->remote.info_str,
+ sizeof(exten_state_data.remote));
+
if ((exten_state_data.exten_state = ast_extension_state_extended(
NULL, exten_state_sub->context, exten_state_sub->exten, &info)) < 0) {
@@ -277,8 +206,12 @@ static void send_notify(struct exten_state_subscription *exten_state_sub, const
return;
}
+ exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
+ "exten_state", 1024, 1024);
+
exten_state_data.device_state_info = info;
create_send_notify(exten_state_sub, reason, evsub_state, &exten_state_data);
+ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data.pool);
}
struct notify_task_data {
@@ -300,6 +233,7 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten
{
struct notify_task_data *task_data =
ao2_alloc(sizeof(*task_data), notify_task_data_destructor);
+ struct pjsip_dialog *dlg;
if (!task_data) {
ast_log(LOG_WARNING, "Unable to create notify task data\n");
@@ -320,6 +254,12 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten
ao2_ref(task_data->exten_state_data.device_state_info, +1);
}
+ dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub);
+ ast_copy_pj_str(task_data->exten_state_data.local, &dlg->local.info_str,
+ sizeof(task_data->exten_state_data.local));
+ ast_copy_pj_str(task_data->exten_state_data.remote, &dlg->remote.info_str,
+ sizeof(task_data->exten_state_data.remote));
+
if ((info->exten_state == AST_EXTENSION_DEACTIVATED) ||
(info->exten_state == AST_EXTENSION_REMOVED)) {
task_data->evsub_state = PJSIP_EVSUB_STATE_TERMINATED;
@@ -334,9 +274,16 @@ static int notify_task(void *obj)
{
RAII_VAR(struct notify_task_data *, task_data, obj, ao2_cleanup);
+ /* Pool allocation has to happen here so that we allocate within a PJLIB thread */
+ task_data->exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
+ "exten_state", 1024, 1024);
+
create_send_notify(task_data->exten_state_sub, task_data->evsub_state ==
PJSIP_EVSUB_STATE_TERMINATED ? "noresource" : NULL,
task_data->evsub_state, &task_data->exten_state_data);
+
+ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(),
+ task_data->exten_state_data.pool);
return 0;
}
@@ -527,107 +474,19 @@ static void to_ami(struct ast_sip_subscription *sub,
exten_state_sub->last_exten_state));
}
-#define DEFAULT_PRESENCE_BODY "application/pidf+xml"
-
-/*!
- * \internal
- * \brief Create and register a subscription handler.
- *
- * Creates a subscription handler that can be registered with the pub/sub
- * framework for the given event_name and accept value.
- */
-static struct ast_sip_subscription_handler *create_and_register_handler(
- const char *event_name, const char *accept)
+static int load_module(void)
{
- struct ast_sip_subscription_handler *handler =
- ao2_alloc(sizeof(*handler), NULL);
-
- if (!handler) {
- return NULL;
- }
-
- handler->event_name = event_name;
- handler->accept[0] = accept;
- if (!strcmp(accept, DEFAULT_PRESENCE_BODY)) {
- handler->handles_default_accept = 1;
- }
-
- handler->subscription_shutdown = subscription_shutdown;
- handler->new_subscribe = new_subscribe;
- handler->resubscribe = resubscribe;
- handler->subscription_timeout = subscription_timeout;
- handler->subscription_terminated = subscription_terminated;
- handler->to_ami = to_ami;
-
- if (ast_sip_register_subscription_handler(handler)) {
+ if (ast_sip_register_subscription_handler(&presence_handler)) {
ast_log(LOG_WARNING, "Unable to register subscription handler %s\n",
- handler->event_name);
- ao2_cleanup(handler);
- return NULL;
+ presence_handler.event_name);
+ return AST_MODULE_LOAD_DECLINE;
}
-
- return handler;
-}
-
-int ast_sip_register_exten_state_provider(struct ast_sip_exten_state_provider *obj)
-{
- if (ast_strlen_zero(obj->type)) {
- ast_log(LOG_WARNING, "Type not specified on provider for event %s\n",
- obj->event_name);
- return -1;
- }
-
- if (ast_strlen_zero(obj->subtype)) {
- ast_log(LOG_WARNING, "Subtype not specified on provider for event %s\n",
- obj->event_name);
- return -1;
- }
-
- if (!obj->create_body) {
- ast_log(LOG_WARNING, "Body handler not specified on provide for event %s\n",
- obj->event_name);
- return -1;
- }
-
- if (!(obj->handler = create_and_register_handler(obj->event_name, obj->body_type))) {
- ast_log(LOG_WARNING, "Handler could not be registered for provider event %s\n",
- obj->event_name);
- return -1;
- }
-
- /* scope to avoid mix declarations */
- {
- SCOPED_LOCK(lock, &providers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
- AST_RWLIST_INSERT_TAIL(&providers, obj, next);
- ast_module_ref(ast_module_info->self);
- }
-
- return 0;
-}
-
-void ast_sip_unregister_exten_state_provider(struct ast_sip_exten_state_provider *obj)
-{
- struct ast_sip_exten_state_provider *i;
- SCOPED_LOCK(lock, &providers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&providers, i, next) {
- if (i == obj) {
- ast_sip_unregister_subscription_handler(i->handler);
- ao2_cleanup(i->handler);
- AST_RWLIST_REMOVE_CURRENT(next);
- ast_module_unref(ast_module_info->self);
- break;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
-}
-
-static int load_module(void)
-{
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
+ ast_sip_unregister_subscription_handler(&presence_handler);
return 0;
}
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index bb12aa5e9..9137b677b 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -31,6 +31,7 @@
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/astobj2.h"
@@ -43,6 +44,10 @@ AO2_GLOBAL_OBJ_STATIC(unsolicited_mwi);
#define STASIS_BUCKETS 13
#define MWI_BUCKETS 53
+
+#define MWI_TYPE "application"
+#define MWI_SUBTYPE "simple-message-summary"
+
static void mwi_subscription_shutdown(struct ast_sip_subscription *sub);
static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
pjsip_rx_data *rdata);
@@ -58,8 +63,8 @@ static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf);
static struct ast_sip_subscription_handler mwi_handler = {
.event_name = "message-summary",
- .accept = { "application/simple-message-summary", },
- .handles_default_accept = 1,
+ .accept = { MWI_TYPE"/"MWI_SUBTYPE, },
+ .default_accept = MWI_TYPE"/"MWI_SUBTYPE,
.subscription_shutdown = mwi_subscription_shutdown,
.new_subscribe = mwi_new_subscribe,
.resubscribe = mwi_resubscribe,
@@ -223,17 +228,11 @@ static int mwi_sub_cmp(void *obj, void *arg, int flags)
return strcmp(mwi_sub1->id, mwi_sub2->id) ? 0 : CMP_MATCH;
}
-struct message_accumulator {
- int old_msgs;
- int new_msgs;
- const char *reason;
-};
-
static int get_message_count(void *obj, void *arg, int flags)
{
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
struct mwi_stasis_subscription *mwi_stasis = obj;
- struct message_accumulator *counter = arg;
+ struct ast_sip_message_accumulator *counter = arg;
struct ast_mwi_state *mwi_state;
msg = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(), mwi_stasis->mailbox);
@@ -366,11 +365,7 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsu
static void send_mwi_notify(struct mwi_subscription *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 },
- };
- struct message_accumulator counter = {
+ struct ast_sip_message_accumulator counter = {
.old_msgs = 0,
.new_msgs = 0,
};
@@ -378,6 +373,13 @@ static void send_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state stat
pjsip_tx_data *tdata;
pj_str_t reason_str;
pj_str_t pj_body;
+ const char *type = sub->is_solicited ?
+ ast_sip_subscription_get_body_type(sub->sip_sub) :
+ MWI_TYPE;
+ const char *subtype = sub->is_solicited ?
+ ast_sip_subscription_get_body_subtype(sub->sip_sub) :
+ MWI_SUBTYPE;
+ pjsip_media_type mwi_type = { { 0,}, };
ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter);
@@ -385,11 +387,17 @@ static void send_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state stat
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);
+
+ if (ast_sip_pubsub_generate_body_content(type, subtype, &counter, &body)) {
+ ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
+ return;
+ }
+
pj_cstr(&pj_body, ast_str_buffer(body));
+ pj_cstr(&mwi_type.type, type);
+ pj_cstr(&mwi_type.subtype, subtype);
- ast_debug(5, "Sending %s MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
+ ast_debug(5, "Sending %s MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
sub->is_solicited ? "solicited" : "unsolicited", sub->id, counter.new_msgs,
counter.old_msgs);
diff --git a/res/res_pjsip_mwi_body_generator.c b/res/res_pjsip_mwi_body_generator.c
new file mode 100644
index 000000000..9a721dbf4
--- /dev/null
+++ b/res/res_pjsip_mwi_body_generator.c
@@ -0,0 +1,112 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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_pjsip</depend>
+ <depend>res_pjsip_pubsub</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_simple.h>
+#include <pjlib.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
+#include "asterisk/module.h"
+#include "asterisk/strings.h"
+
+#define MWI_TYPE "application"
+#define MWI_SUBTYPE "simple-message-summary"
+
+static void *mwi_allocate_body(void *data)
+{
+ struct ast_str **mwi_str;
+
+ mwi_str = ast_malloc(sizeof(*mwi_str));
+ if (!mwi_str) {
+ return NULL;
+ }
+ *mwi_str = ast_str_create(64);
+ if (!*mwi_str) {
+ ast_free(mwi_str);
+ return NULL;
+ }
+ return mwi_str;
+}
+
+static int mwi_generate_body_content(void *body, void *data)
+{
+ struct ast_str **mwi = body;
+ struct ast_sip_message_accumulator *counter = data;
+
+ ast_str_append(mwi, 0, "Messages-Waiting: %s\r\n",
+ counter->new_msgs ? "yes" : "no");
+ ast_str_append(mwi, 0, "Voice-Message: %d/%d (0/0)\r\n",
+ counter->new_msgs, counter->old_msgs);
+
+ return 0;
+}
+
+static void mwi_to_string(void *body, struct ast_str **str)
+{
+ struct ast_str **mwi = body;
+
+ ast_str_set(str, 0, "%s", ast_str_buffer(*mwi));
+}
+
+static void mwi_destroy_body(void *body)
+{
+ struct ast_str **mwi = body;
+
+ ast_free(*mwi);
+ ast_free(mwi);
+}
+
+static struct ast_sip_pubsub_body_generator mwi_generator = {
+ .type = MWI_TYPE,
+ .subtype = MWI_SUBTYPE,
+ .allocate_body = mwi_allocate_body,
+ .generate_body_content = mwi_generate_body_content,
+ .to_string = mwi_to_string,
+ .destroy_body = mwi_destroy_body,
+};
+
+static int load_module(void)
+{
+ if (ast_sip_pubsub_register_body_generator(&mwi_generator)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_pubsub_unregister_body_generator(&mwi_generator);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP MWI resource",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_pjsip_pidf.c b/res/res_pjsip_pidf.c
deleted file mode 100644
index 2a22c134a..000000000
--- a/res/res_pjsip_pidf.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2013, Digium, Inc.
- *
- * Kevin Harwell <kharwell@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_pjsip</depend>
- <depend>res_pjsip_pubsub</depend>
- <depend>res_pjsip_exten_state</depend>
- <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-#include <pjsip.h>
-#include <pjsip_simple.h>
-#include <pjlib.h>
-
-#include "asterisk/module.h"
-#include "asterisk/res_pjsip.h"
-#include "asterisk/res_pjsip_exten_state.h"
-
-enum state {
- NOTIFY_OPEN,
- NOTIFY_INUSE,
- NOTIFY_CLOSED
-};
-
-static void exten_state_to_str(int state, char **statestring, char **pidfstate,
- char **pidfnote, int *local_state)
-{
- switch (state) {
- case AST_EXTENSION_RINGING:
- *statestring = "early";
- *local_state = NOTIFY_INUSE;
- *pidfstate = "busy";
- *pidfnote = "Ringing";
- break;
- case AST_EXTENSION_INUSE:
- *statestring = "confirmed";
- *local_state = NOTIFY_INUSE;
- *pidfstate = "busy";
- *pidfnote = "On the phone";
- break;
- case AST_EXTENSION_BUSY:
- *statestring = "confirmed";
- *local_state = NOTIFY_CLOSED;
- *pidfstate = "busy";
- *pidfnote = "On the phone";
- break;
- case AST_EXTENSION_UNAVAILABLE:
- *statestring = "terminated";
- *local_state = NOTIFY_CLOSED;
- *pidfstate = "away";
- *pidfnote = "Unavailable";
- break;
- case AST_EXTENSION_ONHOLD:
- *statestring = "confirmed";
- *local_state = NOTIFY_CLOSED;
- *pidfstate = "busy";
- *pidfnote = "On hold";
- break;
- case AST_EXTENSION_NOT_INUSE:
- default:
- /* Default setting */
- *statestring = "terminated";
- *local_state = NOTIFY_OPEN;
- *pidfstate = "--";
- *pidfnote ="Ready";
-
- break;
- }
-}
-
-static pj_xml_attr *create_attr(pj_pool_t *pool, pj_xml_node *node,
- const char *name, const char *value)
-{
- pj_xml_attr *attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr);
-
- pj_strdup2(pool, &attr->name, name);
- pj_strdup2(pool, &attr->value, value);
-
- pj_xml_add_attr(node, attr);
- return attr;
-}
-
-static pj_xml_node *create_node(pj_pool_t *pool, pj_xml_node *parent,
- const char* name)
-{
- pj_xml_node *node = PJ_POOL_ALLOC_T(pool, pj_xml_node);
-
- pj_list_init(&node->attr_head);
- pj_list_init(&node->node_head);
-
- pj_strdup2(pool, &node->name, name);
-
- node->content.ptr = NULL;
- node->content.slen = 0;
-
- pj_xml_add_node(parent, node);
- return node;
-}
-
-static void find_node_attr(pj_pool_t* pool, pj_xml_node *parent,
- const char *node_name, const char *attr_name,
- pj_xml_node **node, pj_xml_attr **attr)
-{
- pj_str_t name;
-
- if (!(*node = pj_xml_find_node(parent, pj_cstr(&name, node_name)))) {
- *node = create_node(pool, parent, node_name);
- }
-
- if (!(*attr = pj_xml_find_attr(*node, pj_cstr(&name, attr_name), NULL))) {
- *attr = create_attr(pool, *node, attr_name, "");
- }
-}
-
-/*!
- * \internal
- * \brief Adds non standard elements to the xml body
- *
- * This is some code that was part of the original chan_sip implementation
- * that is not part of the RFC 3863 definition, but we are keeping available
- * for backward compatability. The original comment stated that Eyebeam
- * supports this format.
-
- */
-static void add_non_standard(pj_pool_t *pool, pj_xml_node *node, const char *pidfstate)
-{
- static const char *XMLNS_PP = "xmlns:pp";
- static const char *XMLNS_PERSON = "urn:ietf:params:xml:ns:pidf:person";
-
- static const char *XMLNS_ES = "xmlns:es";
- static const char *XMLNS_RPID_STATUS = "urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status";
-
- static const char *XMLNS_EP = "xmlns:ep";
- static const char *XMLNS_RPID_PERSON = "urn:ietf:params:xml:ns:pidf:rpid:rpid-person";
-
- pj_xml_node *person = create_node(pool, node, "pp:person");
- pj_xml_node *status = create_node(pool, person, "status");
-
- if (pidfstate[0] != '-') {
- pj_xml_node *activities = create_node(pool, status, "ep:activities");
- size_t str_size = sizeof("ep:") + strlen(pidfstate);
-
- activities->content.ptr = pj_pool_alloc(pool, str_size);
- activities->content.slen = pj_ansi_snprintf(activities->content.ptr, str_size,
- "ep:%s", pidfstate);
- }
-
- create_attr(pool, node, XMLNS_PP, XMLNS_PERSON);
- create_attr(pool, node, XMLNS_ES, XMLNS_RPID_STATUS);
- create_attr(pool, node, XMLNS_EP, XMLNS_RPID_PERSON);
-}
-
-static void release_pool(void *obj)
-{
- pj_pool_t *pool = obj;
-
- pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
-}
-
-/*!
- * \internal
- * \brief Convert angle brackets in input into escaped forms suitable for XML
- *
- * \param input Raw input string
- * \param output Sanitized string
- * \param len Size of output buffer
- */
-static void sanitize_xml(const char *input, char *output, size_t len)
-{
- char *copy = ast_strdupa(input);
- char *break_point;
-
- output[0] = '\0';
-
- while ((break_point = strpbrk(copy, "<>\"&'"))) {
- char to_escape = *break_point;
-
- *break_point = '\0';
- strncat(output, copy, len);
-
- switch (to_escape) {
- case '<':
- strncat(output, "&lt;", len);
- break;
- case '>':
- strncat(output, "&gt;", len);
- break;
- case '"':
- strncat(output, "&quot;", len);
- break;
- case '&':
- strncat(output, "&amp;", len);
- break;
- case '\'':
- strncat(output, "&apos;", len);
- break;
- };
-
- copy = break_point + 1;
- }
-
- /* Be sure to copy everything after the final bracket */
- if (*copy) {
- strncat(output, copy, len);
- }
-}
-
-static int pidf_xml_create_body(struct ast_sip_exten_state_data *data, const char *local,
- const char *remote, struct ast_str **body_text)
-{
- pjpidf_pres *pres;
- pjpidf_tuple *tuple;
- pj_str_t entity, note, id, contact, priority;
- char *statestring = NULL, *pidfstate = NULL, *pidfnote = NULL;
- int local_state, size;
- char sanitized[PJSIP_MAX_URL_SIZE];
-
- RAII_VAR(pj_pool_t *, pool,
- pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
- "pidf", 1024, 1024), release_pool);
-
- exten_state_to_str(data->exten_state, &statestring, &pidfstate,
- &pidfnote, &local_state);
-
- if (!(pres = pjpidf_create(pool, pj_cstr(&entity, local)))) {
- ast_log(LOG_WARNING, "Unable to create PIDF presence\n");
- return -1;
- }
-
- add_non_standard(pool, pres, pidfstate);
-
- if (!pjpidf_pres_add_note(pool, pres, pj_cstr(&note, pidfnote))) {
- ast_log(LOG_WARNING, "Unable to add note to PIDF presence\n");
- return -1;
- }
-
- if (!(tuple = pjpidf_pres_add_tuple(pool, pres, pj_cstr(&id, data->exten)))) {
- ast_log(LOG_WARNING, "Unable to create PIDF tuple\n");
- return -1;
- }
-
- sanitize_xml(remote, sanitized, sizeof(sanitized));
- pjpidf_tuple_set_contact(pool, tuple, pj_cstr(&contact, sanitized));
- pjpidf_tuple_set_contact_prio(pool, tuple, pj_cstr(&priority, "1"));
- pjpidf_status_set_basic_open(pjpidf_tuple_get_status(tuple),
- local_state == NOTIFY_OPEN);
-
- if (!(size = pjpidf_print(pres, ast_str_buffer(*body_text),
- ast_str_size(*body_text)))) {
- ast_log(LOG_WARNING, "PIDF body text too large\n");
- return -1;
- }
- *(ast_str_buffer(*body_text) + size) = '\0';
- ast_str_update(*body_text);
-
- return 0;
-}
-
-static struct ast_sip_exten_state_provider pidf_xml_provider = {
- .event_name = "presence",
- .type = "application",
- .subtype = "pidf+xml",
- .body_type = "application/pidf+xml",
- .create_body = pidf_xml_create_body
-};
-
-static int xpidf_xml_create_body(struct ast_sip_exten_state_data *data, const char *local,
- const char *remote, struct ast_str **body_text)
-{
- static pj_str_t STR_ADDR_PARAM = { ";user=ip", 8 };
- pjxpidf_pres *pres;
- pj_xml_attr *attr;
- pj_str_t name, uri;
- char *statestring = NULL, *pidfstate = NULL, *pidfnote = NULL;
- int local_state, size;
- char sanitized[PJSIP_MAX_URL_SIZE];
- pj_xml_node *atom;
- pj_xml_node *address;
- pj_xml_node *status;
- pj_xml_node *msnsubstatus;
-
- RAII_VAR(pj_pool_t *, pool,
- pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
- "pidf", 1024, 1024), release_pool);
-
- exten_state_to_str(data->exten_state, &statestring, &pidfstate,
- &pidfnote, &local_state);
-
- if (!(pres = pjxpidf_create(pool, pj_cstr(&name, local)))) {
- ast_log(LOG_WARNING, "Unable to create PIDF presence\n");
- return -1;
- }
-
- find_node_attr(pool, pres, "atom", "id", &atom, &attr);
- pj_strdup2(pool, &attr->value, data->exten);
-
- find_node_attr(pool, atom, "address", "uri", &address, &attr);
-
- sanitize_xml(remote, sanitized, sizeof(sanitized));
-
- uri.ptr = (char*) pj_pool_alloc(pool, strlen(sanitized) + STR_ADDR_PARAM.slen);
- pj_strcpy2( &uri, sanitized);
-
- pj_strcat( &uri, &STR_ADDR_PARAM);
- pj_strdup(pool, &attr->value, &uri);
-
- create_attr(pool, address, "priority", "0.80000");
-
- find_node_attr(pool, address, "status", "status", &status, &attr);
- pj_strdup2(pool, &attr->value,
- (local_state == NOTIFY_OPEN) ? "open" :
- (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
-
- find_node_attr(pool, address, "msnsubstatus", "substatus", &msnsubstatus, &attr);
- pj_strdup2(pool, &attr->value,
- (local_state == NOTIFY_OPEN) ? "online" :
- (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
-
- if (!(size = pjxpidf_print(pres, ast_str_buffer(*body_text),
- ast_str_size(*body_text)))) {
- ast_log(LOG_WARNING, "XPIDF body text too large\n");
- return -1;
- }
-
- *(ast_str_buffer(*body_text) + size) = '\0';
- ast_str_update(*body_text);
-
- return 0;
-}
-
-static struct ast_sip_exten_state_provider xpidf_xml_provider = {
- .event_name = "presence",
- .type = "application",
- .subtype = "xpidf+xml",
- .body_type = "application/xpidf+xml",
- .create_body = xpidf_xml_create_body
-};
-
-static struct ast_sip_exten_state_provider cpim_pidf_xml_provider = {
- .event_name = "presence",
- .type = "application",
- .subtype = "cpim-pidf+xml",
- .body_type = "application/cpim-pidf+xml",
- .create_body = xpidf_xml_create_body,
-};
-
-static int load_module(void)
-{
- if (ast_sip_register_exten_state_provider(&pidf_xml_provider)) {
- ast_log(LOG_WARNING, "Unable to load provider event_name=%s, body_type=%s",
- pidf_xml_provider.event_name, pidf_xml_provider.body_type);
- }
-
- if (ast_sip_register_exten_state_provider(&xpidf_xml_provider)) {
- ast_log(LOG_WARNING, "Unable to load provider event_name=%s, body_type=%s",
- xpidf_xml_provider.event_name, xpidf_xml_provider.body_type);
- }
-
- if (ast_sip_register_exten_state_provider(&cpim_pidf_xml_provider)) {
- ast_log(LOG_WARNING, "Unable to load provider event_name=%s, body_type=%s",
- cpim_pidf_xml_provider.event_name, cpim_pidf_xml_provider.body_type);
- }
-
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-static int unload_module(void)
-{
- ast_sip_unregister_exten_state_provider(&cpim_pidf_xml_provider);
- ast_sip_unregister_exten_state_provider(&xpidf_xml_provider);
- ast_sip_unregister_exten_state_provider(&pidf_xml_provider);
-
- return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State PIDF Provider",
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
-);
diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c
new file mode 100644
index 000000000..50e8be9e7
--- /dev/null
+++ b/res/res_pjsip_pidf_body_generator.c
@@ -0,0 +1,135 @@
+/*
+ * asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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_pjsip</depend>
+ <depend>res_pjsip_pubsub</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_simple.h>
+#include <pjlib.h>
+
+#include "asterisk/module.h"
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/res_pjsip_presence_xml.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
+
+static void *pidf_allocate_body(void *data)
+{
+ struct ast_sip_exten_state_data *state_data = data;
+ pjpidf_pres *pres;
+ pj_str_t entity;
+
+ pres = pjpidf_create(state_data->pool, pj_cstr(&entity, state_data->local));
+
+ return pres;
+}
+
+static int pidf_generate_body_content(void *body, void *data)
+{
+ pjpidf_tuple *tuple;
+ pj_str_t note, id, contact, priority;
+ char *statestring = NULL, *pidfstate = NULL, *pidfnote = NULL;
+ enum ast_sip_pidf_state local_state;
+ char sanitized[PJSIP_MAX_URL_SIZE];
+ pjpidf_pres *pres = body;
+ struct ast_sip_exten_state_data *state_data = data;
+
+ ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
+ &pidfstate, &pidfnote, &local_state);
+
+ if (!pjpidf_pres_add_note(state_data->pool, pres, pj_cstr(&note, pidfnote))) {
+ ast_log(LOG_WARNING, "Unable to add note to PIDF presence\n");
+ return -1;
+ }
+
+ if (!(tuple = pjpidf_pres_add_tuple(state_data->pool, pres,
+ pj_cstr(&id, state_data->exten)))) {
+ ast_log(LOG_WARNING, "Unable to create PIDF tuple\n");
+ return -1;
+ }
+
+ ast_sip_sanitize_xml(state_data->remote, sanitized, sizeof(sanitized));
+ pjpidf_tuple_set_contact(state_data->pool, tuple, pj_cstr(&contact, sanitized));
+ pjpidf_tuple_set_contact_prio(state_data->pool, tuple, pj_cstr(&priority, "1"));
+ pjpidf_status_set_basic_open(pjpidf_tuple_get_status(tuple),
+ local_state == NOTIFY_OPEN);
+
+ return 0;
+}
+
+#define MAX_STRING_GROWTHS 3
+
+static void pidf_to_string(void *body, struct ast_str **str)
+{
+ int size;
+ int growths = 0;
+ pjpidf_pres *pres = body;
+
+ do {
+ size = pjpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str) - 1);
+ if (size < 0) {
+ ast_str_make_space(str, ast_str_size(*str) * 2);
+ ++growths;
+ return;
+ }
+ } while (size < 0 && growths < MAX_STRING_GROWTHS);
+
+ if (size < 0) {
+ ast_log(LOG_WARNING, "PIDF body text too large\n");
+ return;
+ }
+
+ *(ast_str_buffer(*str) + size) = '\0';
+ ast_str_update(*str);
+}
+
+static struct ast_sip_pubsub_body_generator pidf_body_generator = {
+ .type = "application",
+ .subtype = "pidf+xml",
+ .allocate_body = pidf_allocate_body,
+ .generate_body_content = pidf_generate_body_content,
+ .to_string = pidf_to_string,
+ /* No need for a destroy_body callback since we use a pool */
+};
+
+static int load_module(void)
+{
+ if (ast_sip_pubsub_register_body_generator(&pidf_body_generator)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_pubsub_unregister_body_generator(&pidf_body_generator);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State PIDF Provider",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_pjsip_pidf_eyebeam_body_supplement.c b/res/res_pjsip_pidf_eyebeam_body_supplement.c
new file mode 100644
index 000000000..042cbf5e8
--- /dev/null
+++ b/res/res_pjsip_pidf_eyebeam_body_supplement.c
@@ -0,0 +1,113 @@
+/*
+ * asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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_pjsip</depend>
+ <depend>res_pjsip_pubsub</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_simple.h>
+#include <pjlib.h>
+
+#include "asterisk/module.h"
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/res_pjsip_presence_xml.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
+
+/*!
+ * \internal
+ * \brief Adds non standard elements to the xml body
+ *
+ * This is some code that was part of the original chan_sip implementation
+ * that is not part of the RFC 3863 definition, but we are keeping available
+ * for backward compatability. The original comment stated that Eyebeam
+ * supports this format.
+ */
+static void add_eyebeam(pj_pool_t *pool, pj_xml_node *node, const char *pidfstate)
+{
+ static const char *XMLNS_PP = "xmlns:pp";
+ static const char *XMLNS_PERSON = "urn:ietf:params:xml:ns:pidf:person";
+
+ static const char *XMLNS_ES = "xmlns:es";
+ static const char *XMLNS_RPID_STATUS = "urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status";
+
+ static const char *XMLNS_EP = "xmlns:ep";
+ static const char *XMLNS_RPID_PERSON = "urn:ietf:params:xml:ns:pidf:rpid:rpid-person";
+
+ pj_xml_node *person = ast_sip_presence_xml_create_node(pool, node, "pp:person");
+ pj_xml_node *status = ast_sip_presence_xml_create_node(pool, person, "status");
+
+ if (pidfstate[0] != '-') {
+ pj_xml_node *activities = ast_sip_presence_xml_create_node(pool, status, "ep:activities");
+ size_t str_size = sizeof("ep:") + strlen(pidfstate);
+
+ activities->content.ptr = pj_pool_alloc(pool, str_size);
+ activities->content.slen = pj_ansi_snprintf(activities->content.ptr, str_size,
+ "ep:%s", pidfstate);
+ }
+
+ ast_sip_presence_xml_create_attr(pool, node, XMLNS_PP, XMLNS_PERSON);
+ ast_sip_presence_xml_create_attr(pool, node, XMLNS_ES, XMLNS_RPID_STATUS);
+ ast_sip_presence_xml_create_attr(pool, node, XMLNS_EP, XMLNS_RPID_PERSON);
+}
+
+static int pidf_supplement_body(void *body, void *data)
+{
+ pjpidf_pres *pres = body;
+ struct ast_sip_exten_state_data *state_data = data;
+ char *statestring = NULL, *pidfstate = NULL, *pidfnote = NULL;
+ enum ast_sip_pidf_state local_state;
+
+ ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
+ &pidfstate, &pidfnote, &local_state);
+
+ add_eyebeam(state_data->pool, pres, pidfstate);
+ return 0;
+}
+
+static struct ast_sip_pubsub_body_supplement pidf_supplement = {
+ .type = "application",
+ .subtype = "pidf+xml",
+ .supplement_body = pidf_supplement_body,
+};
+
+static int load_module(void)
+{
+ if (ast_sip_pubsub_register_body_supplement(&pidf_supplement)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_pubsub_unregister_body_supplement(&pidf_supplement);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP PIDF Eyebeam supplement",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 5bc2cb468..f10bf41fc 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -81,6 +81,8 @@ static struct pjsip_module pubsub_module = {
.on_rx_request = pubsub_on_rx_request,
};
+#define MOD_DATA_BODY_GENERATOR "sub_body_generator"
+
static const pj_str_t str_event_name = { "Event", 5 };
/*! \brief Scheduler used for automatically expiring publications */
@@ -195,6 +197,8 @@ struct ast_sip_subscription {
pjsip_evsub *evsub;
/*! The underlying PJSIP dialog */
pjsip_dialog *dlg;
+ /*! Body generaator for NOTIFYs */
+ struct ast_sip_pubsub_body_generator *body_generator;
/*! Next item in the list */
AST_LIST_ENTRY(ast_sip_subscription) next;
};
@@ -206,6 +210,9 @@ static const char *sip_subscription_roles_map[] = {
AST_RWLIST_HEAD_STATIC(subscriptions, ast_sip_subscription);
+AST_RWLIST_HEAD_STATIC(body_generators, ast_sip_pubsub_body_generator);
+AST_RWLIST_HEAD_STATIC(body_supplements, ast_sip_pubsub_body_supplement);
+
static void add_subscription(struct ast_sip_subscription *obj)
{
SCOPED_LOCK(lock, &subscriptions, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
@@ -402,6 +409,8 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
ao2_ref(sub, -1);
return NULL;
}
+ sub->body_generator = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
+ pubsub_module.id, MOD_DATA_BODY_GENERATOR);
sub->role = role;
if (role == AST_SIP_NOTIFIER) {
dlg = ast_sip_create_dialog_uas(endpoint, rdata);
@@ -631,31 +640,35 @@ static void sub_add_handler(struct ast_sip_subscription_handler *handler)
ast_module_ref(ast_module_info->self);
}
-static int sub_handler_exists_for_event_name(const char *event_name)
+static struct ast_sip_subscription_handler *find_sub_handler_for_event_name(const char *event_name)
{
struct ast_sip_subscription_handler *iter;
SCOPED_LOCK(lock, &subscription_handlers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
AST_RWLIST_TRAVERSE(&subscription_handlers, iter, next) {
if (!strcmp(iter->event_name, event_name)) {
- return 1;
+ break;
}
}
- return 0;
+ return iter;
}
int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler)
{
+ pj_str_t event;
pj_str_t accept[AST_SIP_MAX_ACCEPT];
+ struct ast_sip_subscription_handler *existing;
int i;
if (ast_strlen_zero(handler->event_name)) {
- ast_log(LOG_ERROR, "No event package specifief for subscription handler. Cannot register\n");
+ ast_log(LOG_ERROR, "No event package specified for subscription handler. Cannot register\n");
return -1;
}
- if (ast_strlen_zero(handler->accept[0])) {
- ast_log(LOG_ERROR, "Subscription handler must supply at least one 'Accept' format\n");
+ existing = find_sub_handler_for_event_name(handler->event_name);
+ if (existing) {
+ ast_log(LOG_ERROR, "Unable to register subscription handler for event %s."
+ "A handler is already registered\n", handler->event_name);
return -1;
}
@@ -663,19 +676,12 @@ int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *h
pj_cstr(&accept[i], handler->accept[i]);
}
- if (!sub_handler_exists_for_event_name(handler->event_name)) {
- pj_str_t event;
-
- pj_cstr(&event, handler->event_name);
+ pj_cstr(&event, handler->event_name);
- if (!strcmp(handler->event_name, "message-summary")) {
- pjsip_mwi_init_module(ast_sip_get_pjsip_endpoint(), pjsip_evsub_instance());
- } else {
- pjsip_evsub_register_pkg(&pubsub_module, &event, DEFAULT_EXPIRES, i, accept);
- }
+ if (!strcmp(handler->event_name, "message-summary")) {
+ pjsip_mwi_init_module(ast_sip_get_pjsip_endpoint(), pjsip_evsub_instance());
} else {
- pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module, PJSIP_H_ACCEPT, NULL,
- i, accept);
+ pjsip_evsub_register_pkg(&pubsub_module, &event, DEFAULT_EXPIRES, i, accept);
}
sub_add_handler(handler);
@@ -696,48 +702,52 @@ void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler
AST_RWLIST_TRAVERSE_SAFE_END;
}
-static struct ast_sip_subscription_handler *find_sub_handler(const char *event, char accept[AST_SIP_MAX_ACCEPT][64], size_t num_accept)
+static struct ast_sip_pubsub_body_generator *find_body_generator_type_subtype(const char *content_type,
+ const char *content_subtype)
{
- struct ast_sip_subscription_handler *iter;
- int match = 0;
- SCOPED_LOCK(lock, &subscription_handlers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
- AST_RWLIST_TRAVERSE(&subscription_handlers, iter, next) {
- int i;
- int j;
- if (strcmp(event, iter->event_name)) {
- ast_debug(3, "Event %s does not match %s\n", event, iter->event_name);
- continue;
- }
- ast_debug(3, "Event name match: %s = %s\n", event, iter->event_name);
- if (!num_accept && iter->handles_default_accept) {
- /* The SUBSCRIBE contained no Accept headers, and this subscription handler
- * provides the default body type, so it's a match!
- */
+ struct ast_sip_pubsub_body_generator *iter;
+ SCOPED_LOCK(lock, &body_generators, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+
+ AST_LIST_TRAVERSE(&body_generators, iter, list) {
+ if (!strcmp(iter->type, content_type) &&
+ !strcmp(iter->subtype, content_subtype)) {
break;
}
- for (i = 0; i < num_accept; ++i) {
- for (j = 0; j < num_accept; ++j) {
- if (ast_strlen_zero(iter->accept[i])) {
- ast_debug(3, "Breaking because subscription handler has run out of 'accept' types\n");
- break;
- }
- if (!strcmp(accept[j], iter->accept[i])) {
- ast_debug(3, "Accept headers match: %s = %s\n", accept[j], iter->accept[i]);
- match = 1;
- break;
- }
- ast_debug(3, "Accept %s does not match %s\n", accept[j], iter->accept[i]);
- }
- if (match) {
- break;
- }
- }
- if (match) {
+ };
+
+ return iter;
+}
+
+static struct ast_sip_pubsub_body_generator *find_body_generator_accept(const char *accept)
+{
+ char *accept_copy = ast_strdupa(accept);
+ char *subtype = accept_copy;
+ char *type = strsep(&subtype, "/");
+
+ if (ast_strlen_zero(type) || ast_strlen_zero(subtype)) {
+ return NULL;
+ }
+
+ return find_body_generator_type_subtype(type, subtype);
+}
+
+static struct ast_sip_pubsub_body_generator *find_body_generator(char accept[AST_SIP_MAX_ACCEPT][64],
+ size_t num_accept)
+{
+ int i;
+ struct ast_sip_pubsub_body_generator *generator = NULL;
+
+ for (i = 0; i < num_accept; ++i) {
+ generator = find_body_generator_accept(accept[i]);
+ if (generator) {
+ ast_debug(3, "Body generator %p found for accept type %s\n", generator, accept[i]);
break;
+ } else {
+ ast_debug(3, "No body generator found for accept type %s\n", accept[i]);
}
}
- return iter;
+ return generator;
}
static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
@@ -751,6 +761,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
struct ast_sip_subscription *sub;
size_t num_accept_headers;
+ struct ast_sip_pubsub_body_generator *generator;
endpoint = ast_pjsip_rdata_get_endpoint(rdata);
ast_assert(endpoint != NULL);
@@ -778,6 +789,13 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
}
ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
+ handler = find_sub_handler_for_event_name(event);
+ if (!handler) {
+ ast_log(LOG_WARNING, "No registered subscribe handler for event %s\n", event);
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
+ return PJ_TRUE;
+ }
+
accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, rdata->msg_info.msg->hdr.next);
if (accept_header) {
int i;
@@ -787,15 +805,22 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
}
num_accept_headers = accept_header->count;
} else {
- num_accept_headers = 0;
+ /* If a SUBSCRIBE contains no Accept headers, then we must assume that
+ * the default accept type for the event package is to be used.
+ */
+ ast_copy_string(accept[0], handler->default_accept, sizeof(accept[0]));
+ num_accept_headers = 1;
}
- handler = find_sub_handler(event, accept, num_accept_headers);
- if (!handler) {
- ast_log(LOG_WARNING, "No registered subscribe handler for event %s\n", event);
+ generator = find_body_generator(accept, num_accept_headers);
+ if (!generator) {
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
return PJ_TRUE;
}
+
+ ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
+ pubsub_module.id, MOD_DATA_BODY_GENERATOR, generator);
+
sub = handler->new_subscribe(endpoint, rdata);
if (!sub) {
pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
@@ -1063,6 +1088,137 @@ pj_status_t ast_sip_publication_send_response(struct ast_sip_publication *pub, p
return pjsip_tsx_send_msg(tsx, tdata);
}
+int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator)
+{
+ struct ast_sip_pubsub_body_generator *existing;
+ pj_str_t accept;
+ pj_size_t accept_len;
+
+ existing = find_body_generator_type_subtype(generator->type, generator->subtype);
+ if (existing) {
+ ast_log(LOG_WARNING, "Cannot register body generator of %s/%s."
+ "One is already registered.\n", generator->type, generator->subtype);
+ return -1;
+ }
+
+ AST_RWLIST_WRLOCK(&body_generators);
+ AST_LIST_INSERT_HEAD(&body_generators, generator, list);
+ AST_RWLIST_UNLOCK(&body_generators);
+
+ /* Lengths of type and subtype plus space for a slash. pj_str_t is not
+ * null-terminated, so there is no need to allocate for the extra null
+ * byte
+ */
+ accept_len = strlen(generator->type) + strlen(generator->subtype) + 1;
+
+ accept.ptr = alloca(accept_len);
+ accept.slen = accept_len;
+ /* Safe use of sprintf */
+ sprintf(accept.ptr, "%s/%s", generator->type, generator->subtype);
+ pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
+ PJSIP_H_ACCEPT, NULL, 1, &accept);
+
+ return 0;
+}
+
+void ast_sip_pubsub_unregister_body_generator(struct ast_sip_pubsub_body_generator *generator)
+{
+ struct ast_sip_pubsub_body_generator *iter;
+ SCOPED_LOCK(lock, &body_generators, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&body_generators, iter, list) {
+ if (iter == generator) {
+ AST_LIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+}
+
+int ast_sip_pubsub_register_body_supplement(struct ast_sip_pubsub_body_supplement *supplement)
+{
+ AST_RWLIST_WRLOCK(&body_supplements);
+ AST_RWLIST_INSERT_TAIL(&body_supplements, supplement, list);
+ AST_RWLIST_UNLOCK(&body_supplements);
+
+ return 0;
+}
+
+void ast_sip_pubsub_unregister_body_supplement(struct ast_sip_pubsub_body_supplement *supplement)
+{
+ struct ast_sip_pubsub_body_supplement *iter;
+ SCOPED_LOCK(lock, &body_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&body_supplements, iter, list) {
+ if (iter == supplement) {
+ AST_LIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+}
+
+const char *ast_sip_subscription_get_body_type(struct ast_sip_subscription *sub)
+{
+ return sub->body_generator->type;
+}
+
+const char *ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub)
+{
+ return sub->body_generator->subtype;
+}
+
+int ast_sip_pubsub_generate_body_content(const char *type, const char *subtype,
+ void *data, struct ast_str **str)
+{
+ struct ast_sip_pubsub_body_supplement *supplement;
+ struct ast_sip_pubsub_body_generator *generator;
+ int res;
+ void *body;
+
+ generator = find_body_generator_type_subtype(type, subtype);
+ if (!generator) {
+ ast_log(LOG_WARNING, "Unable to find a body generator for %s/%s\n",
+ type, subtype);
+ return -1;
+ }
+
+ body = generator->allocate_body(data);
+ if (!body) {
+ ast_log(LOG_WARNING, "Unable to allocate a NOTIFY body of type %s/%s\n",
+ type, subtype);
+ return -1;
+ }
+
+ if (generator->generate_body_content(body, data)) {
+ res = -1;
+ goto end;
+ }
+
+ AST_RWLIST_RDLOCK(&body_supplements);
+ AST_RWLIST_TRAVERSE(&body_supplements, supplement, list) {
+ if (!strcmp(generator->type, supplement->type) &&
+ !strcmp(generator->subtype, supplement->subtype)) {
+ res = supplement->supplement_body(body, data);
+ if (res) {
+ break;
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&body_supplements);
+
+ if (!res) {
+ generator->to_string(body, str);
+ }
+
+end:
+ if (generator->destroy_body) {
+ generator->destroy_body(body);
+ }
+
+ return res;
+}
+
static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
{
if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
diff --git a/res/res_pjsip_pubsub.exports.in b/res/res_pjsip_pubsub.exports.in
index 15838f2dc..f279b5433 100644
--- a/res/res_pjsip_pubsub.exports.in
+++ b/res/res_pjsip_pubsub.exports.in
@@ -21,6 +21,14 @@
LINKER_SYMBOL_PREFIXast_sip_publication_add_datastore;
LINKER_SYMBOL_PREFIXast_sip_publication_get_datastore;
LINKER_SYMBOL_PREFIXast_sip_publication_remove_datastore;
+ LINKER_SYMBOL_PREFIXast_sip_publication_remove_datastore;
+ LINKER_SYMBOL_PREFIXast_sip_pubsub_register_body_generator;
+ LINKER_SYMBOL_PREFIXast_sip_pubsub_unregister_body_generator;
+ LINKER_SYMBOL_PREFIXast_sip_pubsub_register_body_supplement;
+ LINKER_SYMBOL_PREFIXast_sip_pubsub_unregister_body_supplement;
+ LINKER_SYMBOL_PREFIXast_sip_pubsub_generate_body_content;
+ LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_type;
+ LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_subtype;
local:
*;
};
diff --git a/res/res_pjsip_xpidf_body_generator.c b/res/res_pjsip_xpidf_body_generator.c
new file mode 100644
index 000000000..b68c9bba6
--- /dev/null
+++ b/res/res_pjsip_xpidf_body_generator.c
@@ -0,0 +1,177 @@
+/*
+ * asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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_pjsip</depend>
+ <depend>res_pjsip_pubsub</depend>
+ <depend>res_pjsip_exten_state</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_simple.h>
+#include <pjlib.h>
+
+#include "asterisk/module.h"
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_pubsub.h"
+#include "asterisk/res_pjsip_presence_xml.h"
+#include "asterisk/res_pjsip_body_generator_types.h"
+
+static void *xpidf_allocate_body(void *data)
+{
+ struct ast_sip_exten_state_data *state_data = data;
+ pjxpidf_pres *pres;
+ pj_str_t name;
+
+ pres = pjxpidf_create(state_data->pool, pj_cstr(&name, state_data->local));
+ return pres;
+}
+
+static int xpidf_generate_body_content(void *body, void *data)
+{
+ pjxpidf_pres *pres = body;
+ struct ast_sip_exten_state_data *state_data = data;
+ static pj_str_t STR_ADDR_PARAM = { ";user=ip", 8 };
+ char *statestring = NULL, *pidfstate = NULL, *pidfnote = NULL;
+ pj_xml_attr *attr;
+ enum ast_sip_pidf_state local_state;
+ pj_str_t uri;
+ char sanitized[PJSIP_MAX_URL_SIZE];
+ pj_xml_node *atom;
+ pj_xml_node *address;
+ pj_xml_node *status;
+ pj_xml_node *msnsubstatus;
+
+ ast_sip_presence_exten_state_to_str(state_data->exten_state, &statestring,
+ &pidfstate, &pidfnote, &local_state);
+
+ ast_sip_presence_xml_find_node_attr(state_data->pool, pres, "atom", "id",
+ &atom, &attr);
+ pj_strdup2(state_data->pool, &attr->value, state_data->exten);
+
+ ast_sip_presence_xml_find_node_attr(state_data->pool, atom, "address",
+ "uri", &address, &attr);
+
+ ast_sip_sanitize_xml(state_data->remote, sanitized, sizeof(sanitized));
+
+ uri.ptr = (char*) pj_pool_alloc(state_data->pool,
+ strlen(sanitized) + STR_ADDR_PARAM.slen);
+ pj_strcpy2( &uri, sanitized);
+ pj_strcat( &uri, &STR_ADDR_PARAM);
+ pj_strdup(state_data->pool, &attr->value, &uri);
+
+ ast_sip_presence_xml_create_attr(state_data->pool, address, "priority", "0.80000");
+
+ ast_sip_presence_xml_find_node_attr(state_data->pool, address,
+ "status", "status", &status, &attr);
+ pj_strdup2(state_data->pool, &attr->value,
+ (local_state == NOTIFY_OPEN) ? "open" :
+ (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
+
+ ast_sip_presence_xml_find_node_attr(state_data->pool, address,
+ "msnsubstatus", "substatus", &msnsubstatus, &attr);
+ pj_strdup2(state_data->pool, &attr->value,
+ (local_state == NOTIFY_OPEN) ? "online" :
+ (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
+
+ return 0;
+}
+
+#define MAX_STRING_GROWTHS 3
+
+static void xpidf_to_string(void *body, struct ast_str **str)
+{
+ pjxpidf_pres *pres = body;
+ int growths = 0;
+ int size;
+
+ do {
+
+ size = pjxpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str));
+ if (size < 0) {
+ ast_str_make_space(str, ast_str_size(*str) * 2);
+ ++growths;
+ return;
+ }
+ } while (size < 0 && growths < MAX_STRING_GROWTHS);
+
+ if (size < 0) {
+ ast_log(LOG_WARNING, "XPIDF body text too large\n");
+ return;
+ }
+
+ *(ast_str_buffer(*str) + size) = '\0';
+ ast_str_update(*str);
+}
+
+static struct ast_sip_pubsub_body_generator xpidf_body_generator = {
+ .type = "application",
+ .subtype = "xpidf+xml",
+ .allocate_body = xpidf_allocate_body,
+ .generate_body_content = xpidf_generate_body_content,
+ .to_string = xpidf_to_string,
+ /* No need for a destroy_body callback since we use a pool */
+};
+
+static struct ast_sip_pubsub_body_generator cpim_pidf_body_generator = {
+ .type = "application",
+ .subtype = "cpim-pidf+xml",
+ .allocate_body = xpidf_allocate_body,
+ .generate_body_content = xpidf_generate_body_content,
+ .to_string = xpidf_to_string,
+ /* No need for a destroy_body callback since we use a pool */
+};
+
+static void unregister_all(void)
+{
+ ast_sip_pubsub_unregister_body_generator(&cpim_pidf_body_generator);
+ ast_sip_pubsub_unregister_body_generator(&xpidf_body_generator);
+}
+
+static int load_module(void)
+{
+ if (ast_sip_pubsub_register_body_generator(&xpidf_body_generator)) {
+ goto fail;
+ }
+
+ if (ast_sip_pubsub_register_body_generator(&cpim_pidf_body_generator)) {
+ goto fail;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+
+fail:
+ unregister_all();
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+static int unload_module(void)
+{
+ unregister_all();
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State PIDF Provider",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);