[svn-commits] mmichelson: branch group/rls r420365 - in /team/group/rls: include/asterisk/ ...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Thu Aug 7 13:58:10 CDT 2014
Author: mmichelson
Date: Thu Aug 7 13:58:04 2014
New Revision: 420365
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=420365
Log:
Merge RLS RLMI body generation.
ASTERISK-23867 #close
Reported by Mark Michelson
Review: https://reviewboard.asterisk.org/r/3741
Modified:
team/group/rls/include/asterisk/res_pjsip_presence_xml.h
team/group/rls/include/asterisk/res_pjsip_pubsub.h
team/group/rls/include/asterisk/strings.h
team/group/rls/main/strings.c
team/group/rls/res/res_pjsip_dialog_info_body_generator.c
team/group/rls/res/res_pjsip_pidf_body_generator.c
team/group/rls/res/res_pjsip_pubsub.c
team/group/rls/res/res_pjsip_xpidf_body_generator.c
Modified: team/group/rls/include/asterisk/res_pjsip_presence_xml.h
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/include/asterisk/res_pjsip_presence_xml.h?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/include/asterisk/res_pjsip_presence_xml.h (original)
+++ team/group/rls/include/asterisk/res_pjsip_presence_xml.h Thu Aug 7 13:58:04 2014
@@ -15,6 +15,21 @@
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
+
+/*!
+ * \brief The length of the XML prolog when printing
+ * presence or other XML in PJSIP.
+ *
+ * When calling any variant of pj_xml_print(), the documentation
+ * claims that it will return -1 if the provided buffer is not
+ * large enough. However, if the XML prolog is requested to be
+ * printed, then the length of the XML prolog is returned upon
+ * failure instead of -1.
+ *
+ * This constant is useful to check against when trying to determine
+ * if printing XML succeeded or failed.
+ */
+#define AST_PJSIP_XML_PROLOG_LEN 39
/*!
* PIDF state
Modified: team/group/rls/include/asterisk/res_pjsip_pubsub.h
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/include/asterisk/res_pjsip_pubsub.h?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/include/asterisk/res_pjsip_pubsub.h (original)
+++ team/group/rls/include/asterisk/res_pjsip_pubsub.h Thu Aug 7 13:58:04 2014
@@ -349,10 +349,9 @@
/*!
* \brief Notify a SIP subscription of a state change.
*
- * This will create a NOTIFY body to be sent out for the subscribed resource.
- * On real subscriptions, a NOTIFY request will be generated and sent.
- * On virtual subscriptions, the NOTIFY is saved on the virtual subscription and the
- * parent subscription is alerted.
+ * This tells the pubsub core that the state of a subscribed resource has changed.
+ * The pubsub core will generate an appropriate NOTIFY request to send to the
+ * subscriber.
*
* \param sub The subscription on which a state change is occurring.
* \param notify_data Event package-specific data used to create the NOTIFY body.
@@ -365,7 +364,7 @@
/*!
* \brief Retrieve the local URI for this subscription
*
- * This is the local URI as determined by the underlying SIP dialog.
+ * This is the local URI of the subscribed resource.
*
* \param sub The subscription
* \param[out] buf The buffer into which to store the URI.
Modified: team/group/rls/include/asterisk/strings.h
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/include/asterisk/strings.h?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/include/asterisk/strings.h (original)
+++ team/group/rls/include/asterisk/strings.h Thu Aug 7 13:58:04 2014
@@ -1196,4 +1196,20 @@
*/
void ast_str_container_remove(struct ao2_container *str_container, const char *remove);
+/*!
+ * \brief Create a pseudo-random string of a fixed length.
+ *
+ * This function is useful for generating a string whose randomness
+ * does not need to be across all time and space, does not need to
+ * be cryptographically secure, and needs to fit in a limited space.
+ *
+ * This function will write a null byte at the final position
+ * in the buffer (buf[size - 1]). So if you pass in a size of
+ * 10, then this will generate a random 9-character string.
+ *
+ * \param buf Buffer to write random string into.
+ * \param size The size of the buffer.
+ * \return A pointer to buf
+ */
+char *ast_generate_random_string(char *buf, size_t size);
#endif /* _ASTERISK_STRINGS_H */
Modified: team/group/rls/main/strings.c
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/main/strings.c?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/main/strings.c (original)
+++ team/group/rls/main/strings.c Thu Aug 7 13:58:04 2014
@@ -195,3 +195,15 @@
{
ao2_find(str_container, remove, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
}
+
+char *ast_generate_random_string(char *buf, size_t size)
+{
+ int i;
+
+ for (i = 0; i < size - 1; ++i) {
+ buf[i] = 'a' + (ast_random() % 26);
+ }
+ buf[i] = '\0';
+
+ return buf;
+}
Modified: team/group/rls/res/res_pjsip_dialog_info_body_generator.c
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/res/res_pjsip_dialog_info_body_generator.c?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/res/res_pjsip_dialog_info_body_generator.c (original)
+++ team/group/rls/res/res_pjsip_dialog_info_body_generator.c Thu Aug 7 13:58:04 2014
@@ -156,11 +156,6 @@
*/
#define MAX_STRING_GROWTHS 3
-/* When having pj_xml_print add the XML prolog to the output body the function will return 39
- * instead of -1 if the rest of the document can not be printed into the body.
- */
-#define XML_PROLOG 39
-
static void dialog_info_to_string(void *body, struct ast_str **str)
{
pj_xml_node *dialog_info = body;
@@ -169,13 +164,13 @@
do {
size = pj_xml_print(dialog_info, ast_str_buffer(*str), ast_str_size(*str), PJ_TRUE);
- if (size == XML_PROLOG) {
+ if (size == AST_PJSIP_XML_PROLOG_LEN) {
ast_str_make_space(str, ast_str_size(*str) * 2);
++growths;
}
- } while (size == XML_PROLOG && growths < MAX_STRING_GROWTHS);
-
- if (size == XML_PROLOG) {
+ } while (size == AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
+
+ if (size == AST_PJSIP_XML_PROLOG_LEN) {
ast_log(LOG_WARNING, "dialog-info+xml body text too large\n");
return;
}
Modified: team/group/rls/res/res_pjsip_pidf_body_generator.c
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/res/res_pjsip_pidf_body_generator.c?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/res/res_pjsip_pidf_body_generator.c (original)
+++ team/group/rls/res/res_pjsip_pidf_body_generator.c Thu Aug 7 13:58:04 2014
@@ -81,7 +81,6 @@
}
#define MAX_STRING_GROWTHS 5
-#define XML_PROLOG 39
static void pidf_to_string(void *body, struct ast_str **str)
{
@@ -91,13 +90,13 @@
do {
size = pjpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str) - 1);
- if (size == XML_PROLOG) {
+ if (size == AST_PJSIP_XML_PROLOG_LEN) {
ast_str_make_space(str, ast_str_size(*str) * 2);
++growths;
}
- } while (size == XML_PROLOG && growths < MAX_STRING_GROWTHS);
+ } while (size == AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
- if (size == XML_PROLOG) {
+ if (size == AST_PJSIP_XML_PROLOG_LEN) {
ast_log(LOG_WARNING, "PIDF body text too large\n");
return;
}
Modified: team/group/rls/res/res_pjsip_pubsub.c
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/res/res_pjsip_pubsub.c?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/res/res_pjsip_pubsub.c (original)
+++ team/group/rls/res/res_pjsip_pubsub.c Thu Aug 7 13:58:04 2014
@@ -44,6 +44,7 @@
#include "asterisk/manager.h"
#include "asterisk/test.h"
#include "res_pjsip/include/res_pjsip_private.h"
+#include "asterisk/res_pjsip_presence_xml.h"
/*** DOCUMENTATION
<manager name="PJSIPShowSubscriptionsInbound" language="en_US">
@@ -424,6 +425,12 @@
int body_changed;
/*! The current state of the subscription */
pjsip_evsub_state subscription_state;
+ /*! For lists, the current version to place in the RLMI body */
+ unsigned int version;
+ /*! For lists, indicates if full state should always be communicated. */
+ unsigned int full_state;
+ /*! URI associated with the subscription */
+ pjsip_sip_uri *uri;
/*! Name of resource being subscribed to */
char resource[0];
};
@@ -634,21 +641,22 @@
static struct ast_sip_pubsub_body_generator *subscription_get_generator_from_rdata(pjsip_rx_data *rdata,
const struct ast_sip_subscription_handler *handler)
{
- pjsip_accept_hdr *accept_header;
+ pjsip_accept_hdr *accept_header = (pjsip_accept_hdr *) &rdata->msg_info.msg->hdr;
char accept[AST_SIP_MAX_ACCEPT][64];
size_t num_accept_headers = 0;
- accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, rdata->msg_info.msg->hdr.next);
- if (accept_header) {
+ while ((accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, accept_header->next))) {
int i;
for (i = 0; i < accept_header->count; ++i) {
if (!exceptional_accept(&accept_header->values[i])) {
- ast_copy_pj_str(accept[i], &accept_header->values[i], sizeof(accept[i]));
+ ast_copy_pj_str(accept[num_accept_headers], &accept_header->values[i], sizeof(accept[num_accept_headers]));
++num_accept_headers;
}
}
- } else {
+ }
+
+ if (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.
*/
@@ -666,6 +674,7 @@
*/
struct tree_node {
AST_VECTOR(, struct tree_node *) children;
+ unsigned int full_state;
char resource[0];
};
@@ -705,10 +714,11 @@
*
* \param resource The name of the resource for this tree node.
* \param visited The vector of resources that have been visited.
+ * \param if allocating a list, indicate whether full state is requested in notifications.
* \retval NULL Allocation failure.
* \retval non-NULL The newly-allocated tree_node
*/
-static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited)
+static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state)
{
struct tree_node *node;
@@ -722,6 +732,7 @@
ast_free(node);
return NULL;
}
+ node->full_state = full_state;
if (visited) {
AST_VECTOR_APPEND(visited, resource);
@@ -813,7 +824,7 @@
if (!child_list) {
int resp = handler->notifier->new_subscribe(endpoint, resource);
if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
- current = tree_node_alloc(resource, visited);
+ current = tree_node_alloc(resource, visited, 0);
if (!current) {
ast_debug(1, "Subscription to leaf resource %s was successful, but encountered"
"allocation error afterwards\n", resource);
@@ -828,7 +839,7 @@
}
} else {
ast_debug(1, "Resource %s (child of %s) is a list\n", resource, parent->resource);
- current = tree_node_alloc(resource, visited);
+ current = tree_node_alloc(resource, visited, child_list->full_state);
if (!current) {
ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
continue;
@@ -911,7 +922,7 @@
list = retrieve_resource_list(resource, handler->event_name);
if (!list) {
ast_debug(1, "Subscription to resource %s is not to a list\n", resource);
- tree->root = tree_node_alloc(resource, NULL);
+ tree->root = tree_node_alloc(resource, NULL, 0);
if (!tree->root) {
return 500;
}
@@ -923,7 +934,7 @@
return 500;
}
- tree->root = tree_node_alloc(resource, &visited);
+ tree->root = tree_node_alloc(resource, &visited, list->full_state);
if (!tree->root) {
return 500;
}
@@ -1007,7 +1018,7 @@
{
struct ast_sip_subscription *sub = obj;
- ast_debug(3, "Destroying SIP subscription\n");
+ ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource);
ast_free(sub->body_text);
ao2_cleanup(sub->datastores);
@@ -1017,6 +1028,7 @@
const char *resource, struct sip_subscription_tree *tree)
{
struct ast_sip_subscription *sub;
+ pjsip_sip_uri *contact_uri;
sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor);
if (!sub) {
@@ -1035,6 +1047,11 @@
ao2_ref(sub, -1);
return NULL;
}
+
+ sub->uri = pjsip_sip_uri_create(tree->dlg->pool, PJ_FALSE);
+ contact_uri = pjsip_uri_get_uri(tree->dlg->local.contact->uri);
+ pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, contact_uri);
+ pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);
sub->handler = handler;
sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
@@ -1064,6 +1081,7 @@
return NULL;
}
+ sub->full_state = current->full_state;
sub->body_generator = generator;
for (i = 0; i < AST_VECTOR_SIZE(¤t->children); ++i) {
@@ -1088,6 +1106,23 @@
return sub;
}
+static void shutdown_subscriptions(struct ast_sip_subscription *sub)
+{
+ int i;
+
+ if (AST_VECTOR_SIZE(&sub->children) > 0) {
+ for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
+ shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
+ ao2_cleanup(AST_VECTOR_GET(&sub->children, i));
+ }
+ return;
+ }
+
+ if (sub->handler->subscription_shutdown) {
+ sub->handler->subscription_shutdown(sub);
+ }
+}
+
static void subscription_tree_destructor(void *obj)
{
struct sip_subscription_tree *sub_tree = obj;
@@ -1100,6 +1135,9 @@
if (sub_tree->dlg) {
ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub_tree);
}
+
+ shutdown_subscriptions(sub_tree->root);
+ ao2_cleanup(sub_tree->root);
ast_taskprocessor_unreference(sub_tree->serializer);
ast_module_unref(ast_module_info->self);
@@ -1134,6 +1172,7 @@
}
sub_tree->endpoint = ao2_bump(endpoint);
+ sub_tree->notify_sched_id = -1;
add_subscription(sub_tree);
return sub_tree;
@@ -1484,7 +1523,6 @@
#endif
int res;
- ao2_ref(sub_tree, +1);
res = pjsip_evsub_send_request(sub_tree->evsub, tdata) == PJ_SUCCESS ? 0 : -1;
subscription_persistence_update(sub_tree, NULL);
@@ -1493,71 +1531,439 @@
"Endpoint: %s\r\n",
pjsip_evsub_get_state_name(sub_tree->evsub),
ast_sorcery_object_get_id(endpoint));
- ao2_cleanup(sub_tree);
return res;
}
-static int generate_list_body(struct ast_sip_subscription *sub, struct ast_str **str)
-{
- /* XXX This is where a multipart/related body would be created with
- * an RLMI part and the bodies of all child parts of the subscription.
- * The generated multipart/related body is then saved on sub->body_text.
- *
- * This will be added when working on ASTERISK-23867. For now, this is
- * just a STUB.
+/*!
+ * \brief Add a resource XML element to an RLMI body
+ *
+ * Each resource element represents a subscribed resource in the list. This function currently
+ * will unconditionally add an instance element to each created resource element. Instance
+ * elements refer to later parts in the multipart body.
+ *
+ * \param pool PJLIB allocation pool
+ * \param cid Content-ID header of the resource
+ * \param resource_name Name of the resource
+ * \param resource_uri URI of the resource
+ * \param state State of the subscribed resource
+ */
+static void add_rlmi_resource(pj_pool_t *pool, pj_xml_node *rlmi, const pjsip_generic_string_hdr *cid,
+ const char *resource_name, const pjsip_sip_uri *resource_uri, pjsip_evsub_state state)
+{
+ static pj_str_t cid_name = { "cid", 3 };
+ pj_xml_node *resource;
+ pj_xml_node *name;
+ pj_xml_node *instance;
+ pj_xml_attr *cid_attr;
+ char id[6];
+ char uri[PJSIP_MAX_URL_SIZE];
+
+ /* This creates a string representing the Content-ID without the enclosing < > */
+ const pj_str_t cid_stripped = {
+ .ptr = cid->hvalue.ptr + 1,
+ .slen = cid->hvalue.slen - 2,
+ };
+
+ resource = ast_sip_presence_xml_create_node(pool, rlmi, "resource");
+ name = ast_sip_presence_xml_create_node(pool, resource, "name");
+ instance = ast_sip_presence_xml_create_node(pool, resource, "instance");
+
+ pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, resource_uri, uri, sizeof(uri));
+ ast_sip_presence_xml_create_attr(pool, resource, "uri", uri);
+
+ pj_strdup2(pool, &name->content, resource_name);
+
+ ast_generate_random_string(id, sizeof(id));
+
+ ast_sip_presence_xml_create_attr(pool, instance, "id", id);
+ ast_sip_presence_xml_create_attr(pool, instance, "state",
+ state == PJSIP_EVSUB_STATE_TERMINATED ? "terminated" : "active");
+
+ /* Use the PJLIB-util XML library directly here since we are using a
+ * pj_str_t
*/
- return 0;
-}
-
-static int generate_notify_body(struct ast_sip_subscription *root, struct ast_str **body_text)
-{
+
+ cid_attr = pj_xml_attr_new(pool, &cid_name, &cid_stripped);
+ pj_xml_add_attr(instance, cid_attr);
+}
+
+/*!
+ * \brief A multipart body part and meta-information
+ *
+ * When creating a multipart body part, the end result (the
+ * pjsip_multipart_part) is hard to inspect without undoing
+ * a lot of what was done to create it. Therefore, we use this
+ * structure to store meta-information about the body part.
+ *
+ * The main consumer of this is the creator of the RLMI body
+ * part of a multipart resource list body.
+ */
+struct body_part {
+ /*! Content-ID header for the body part */
+ pjsip_generic_string_hdr *cid;
+ /*! Subscribed resource represented in the body part */
+ const char *resource;
+ /*! URI for the subscribed body part */
+ pjsip_sip_uri *uri;
+ /*! Subscription state of the resource represented in the body part */
+ pjsip_evsub_state state;
+ /*! The actual body part that will be present in the multipart body */
+ pjsip_multipart_part *part;
+};
+
+/*!
+ * \brief Type declaration for container of body part structures
+ */
+AST_VECTOR(body_part_list, struct body_part *);
+
+/*!
+ * \brief Create a Content-ID header
+ *
+ * Content-ID headers are required by RFC2387 for multipart/related
+ * bodies. They serve as identifiers for each part of the multipart body.
+ *
+ * \param pool PJLIB allocation pool
+ * \param sub Subscription to a resource
+ */
+static pjsip_generic_string_hdr *generate_content_id_hdr(pj_pool_t *pool,
+ const struct ast_sip_subscription *sub)
+{
+ static const pj_str_t cid_name = { "Content-ID", 10 };
+ pjsip_generic_string_hdr *cid;
+ char id[6];
+ size_t alloc_size;
+ pj_str_t cid_value;
+
+ /* '<' + '@' + '>' = 3. pj_str_t does not require a null-terminator */
+ alloc_size = sizeof(id) + pj_strlen(&sub->uri->host) + 3;
+ cid_value.ptr = pj_pool_alloc(pool, alloc_size);
+ cid_value.slen = sprintf(cid_value.ptr, "<%s@%.*s>",
+ ast_generate_random_string(id, sizeof(id)),
+ (int) pj_strlen(&sub->uri->host), pj_strbuf(&sub->uri->host));
+ cid = pjsip_generic_string_hdr_create(pool, &cid_name, &cid_value);
+
+ return cid;
+}
+
+static int rlmi_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+ int num_printed;
+ pj_xml_node *rlmi = msg_body->data;
+
+ num_printed = pj_xml_print(rlmi, buf, size, PJ_TRUE);
+ if (num_printed == AST_PJSIP_XML_PROLOG_LEN) {
+ return -1;
+ }
+
+ return num_printed;
+}
+
+static void *rlmi_clone_data(pj_pool_t *pool, const void *data, unsigned len)
+{
+ const pj_xml_node *rlmi = data;
+
+ return pj_xml_clone(pool, rlmi);
+}
+
+/*!
+ * \brief Create an RLMI body part for a multipart resource list body
+ *
+ * RLMI (Resource list meta information) is a special body type that lists
+ * the subscribed resources and tells subscribers the number of subscribed
+ * resources and what other body parts are in the multipart body. The
+ * RLMI body also has a version number that a subscriber can use to ensure
+ * that the locally-stored state corresponds to server state.
+ *
+ * \param pool The allocation pool
+ * \param sub The subscription representing the subscribed resource list
+ * \param body_parts A container of body parts that RLMI will refer to
+ * \param full_state Indicates whether this is a full or partial state notification
+ * \return The multipart part representing the RLMI body
+ */
+static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
+ struct body_part_list *body_parts, unsigned int full_state)
+{
+ static const pj_str_t rlmi_type = { "application", 11 };
+ static const pj_str_t rlmi_subtype = { "rlmi+xml", 8 };
+ pj_xml_node *rlmi;
+ pj_xml_node *name;
+ pjsip_multipart_part *rlmi_part;
+ char version_str[32];
+ char uri[PJSIP_MAX_URL_SIZE];
+ pjsip_generic_string_hdr *cid;
+ int i;
+
+ rlmi = ast_sip_presence_xml_create_node(pool, NULL, "list");
+ ast_sip_presence_xml_create_attr(pool, rlmi, "xmlns", "urn:ietf:params:xml:ns:rlmi");
+
+ ast_sip_subscription_get_local_uri(sub, uri, sizeof(uri));
+ ast_sip_presence_xml_create_attr(pool, rlmi, "uri", uri);
+
+ snprintf(version_str, sizeof(version_str), "%u", sub->version++);
+ ast_sip_presence_xml_create_attr(pool, rlmi, "version", version_str);
+ ast_sip_presence_xml_create_attr(pool, rlmi, "fullState", full_state ? "true" : "false");
+
+ name = ast_sip_presence_xml_create_node(pool, rlmi, "name");
+ pj_strdup2(pool, &name->content, ast_sip_subscription_get_resource_name(sub));
+
+ for (i = 0; i < AST_VECTOR_SIZE(body_parts); ++i) {
+ const struct body_part *part = AST_VECTOR_GET(body_parts, i);
+
+ add_rlmi_resource(pool, rlmi, part->cid, part->resource, part->uri, part->state);
+ }
+
+ rlmi_part = pjsip_multipart_create_part(pool);
+
+ rlmi_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
+ pj_strdup(pool, &rlmi_part->body->content_type.type, &rlmi_type);
+ pj_strdup(pool, &rlmi_part->body->content_type.subtype, &rlmi_subtype);
+ pj_list_init(&rlmi_part->body->content_type.param);
+
+ rlmi_part->body->data = pj_xml_clone(pool, rlmi);
+ rlmi_part->body->clone_data = rlmi_clone_data;
+ rlmi_part->body->print_body = rlmi_print_body;
+
+ cid = generate_content_id_hdr(pool, sub);
+ pj_list_insert_before(&rlmi_part->hdr, cid);
+
+ return rlmi_part;
+}
+
+static pjsip_msg_body *generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root,
+ unsigned int force_full_state);
+
+/*!
+ * \brief Destroy a list of body parts
+ *
+ * \param parts The container of parts to destroy
+ */
+static void free_body_parts(struct body_part_list *parts)
+{
+ int i;
+
+ for (i = 0; i < AST_VECTOR_SIZE(parts); ++i) {
+ struct body_part *part = AST_VECTOR_GET(parts, i);
+ ast_free(part);
+ }
+
+ AST_VECTOR_FREE(parts);
+}
+
+/*!
+ * \brief Allocate and initialize a body part structure
+ *
+ * \param pool PJLIB allocation pool
+ * \param sub Subscription representing a subscribed resource
+ */
+static struct body_part *allocate_body_part(pj_pool_t *pool, const struct ast_sip_subscription *sub)
+{
+ struct body_part *bp;
+
+ bp = ast_calloc(1, sizeof(*bp));
+ if (!bp) {
+ return NULL;
+ }
+
+ bp->cid = generate_content_id_hdr(pool, sub);
+ bp->resource = sub->resource;
+ bp->state = sub->subscription_state;
+ bp->uri = sub->uri;
+
+ return bp;
+}
+
+/*!
+ * \brief Create a multipart body part for a subscribed resource
+ *
+ * \param pool PJLIB allocation pool
+ * \param sub The subscription representing a subscribed resource
+ * \param parts A vector of parts to append the created part to.
+ * \param use_full_state Unused locally, but may be passed to other functions
+ */
+static void build_body_part(pj_pool_t *pool, struct ast_sip_subscription *sub,
+ struct body_part_list *parts, unsigned int use_full_state)
+{
+ struct body_part *bp;
+ pjsip_msg_body *body;
+
+ bp = allocate_body_part(pool, sub);
+ if (!bp) {
+ return;
+ }
+
+ body = generate_notify_body(pool, sub, use_full_state);
+ if (!body) {
+ /* Partial state was requested and the resource has not changed state */
+ ast_free(bp);
+ return;
+ }
+
+ bp->part = pjsip_multipart_create_part(pool);
+ bp->part->body = body;
+ pj_list_insert_before(&bp->part->hdr, bp->cid);
+
+ AST_VECTOR_APPEND(parts, bp);
+}
+
+/*!
+ * \brief Create and initialize the PJSIP multipart body structure for a resource list subscription
+ *
+ * \param pool
+ * \return The multipart message body
+ */
+static pjsip_msg_body *create_multipart_body(pj_pool_t *pool)
+{
+ pjsip_media_type media_type;
+ pjsip_param *media_type_param;
+ char boundary[6];
+ pj_str_t pj_boundary;
+
+ pjsip_media_type_init2(&media_type, "multipart", "related");
+
+ media_type_param = pj_pool_alloc(pool, sizeof(*media_type_param));
+ pj_list_init(media_type_param);
+
+ pj_strdup2(pool, &media_type_param->name, "type");
+ pj_strdup2(pool, &media_type_param->value, "\"application/rlmi+xml\"");
+
+ pj_list_insert_before(&media_type.param, media_type_param);
+
+ pj_cstr(&pj_boundary, ast_generate_random_string(boundary, sizeof(boundary)));
+ return pjsip_multipart_create(pool, &media_type, &pj_boundary);
+}
+
+/*!
+ * \brief Create a resource list body for NOTIFY requests
+ *
+ * Resource list bodies are multipart/related bodies. The first part of the multipart body
+ * is an RLMI body that describes the rest of the parts to come. The other parts of the body
+ * convey state of individual subscribed resources.
+ *
+ * \param pool PJLIB allocation pool
+ * \param sub Subscription details from which to generate body
+ * \param force_full_state If true, ignore resource list settings and send a full state notification
+ * \return The generated multipart/related body
+ */
+static pjsip_msg_body *generate_list_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
+ unsigned int force_full_state)
+{
+ int i;
+ pjsip_multipart_part *rlmi_part;
+ pjsip_msg_body *multipart;
+ struct body_part_list body_parts;
+ unsigned int use_full_state = force_full_state ? 1 : sub->full_state;
+
+ if (AST_VECTOR_INIT(&body_parts, AST_VECTOR_SIZE(&sub->children))) {
+ return NULL;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
+ build_body_part(pool, AST_VECTOR_GET(&sub->children, i), &body_parts, use_full_state);
+ }
+
+ /* This can happen if issuing partial state and no children of the list have changed state */
+ if (AST_VECTOR_SIZE(&body_parts) == 0) {
+ return NULL;
+ }
+
+ multipart = create_multipart_body(pool);
+
+ rlmi_part = build_rlmi_body(pool, sub, &body_parts, use_full_state);
+ if (!rlmi_part) {
+ return NULL;
+ }
+ pjsip_multipart_add_part(pool, multipart, rlmi_part);
+
+ for (i = 0; i < AST_VECTOR_SIZE(&body_parts); ++i) {
+ pjsip_multipart_add_part(pool, multipart, AST_VECTOR_GET(&body_parts, i)->part);
+ }
+
+ free_body_parts(&body_parts);
+ return multipart;
+}
+
+/*!
+ * \brief Create the body for a NOTIFY request.
+ *
+ * \param pool The pool used for allocations
+ * \param root The root of the subscription tree
+ * \param force_full_state If true, ignore resource list settings and send a full state notification
+ */
+static pjsip_msg_body *generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root,
+ unsigned int force_full_state)
+{
+ pjsip_msg_body *body;
+
if (AST_VECTOR_SIZE(&root->children) == 0) {
- /* Not a list. We've already generated the body and saved it on the subscription.
- * Use that directly.
- */
- ast_str_copy_string(body_text, root->body_text);
- root->body_changed = 0;
- return 0;
- }
-
- return generate_list_body(root, body_text);
-}
-
-static int send_notify(struct sip_subscription_tree *sub_tree)
+ if (force_full_state || root->body_changed) {
+ /* Not a list. We've already generated the body and saved it on the subscription.
+ * Use that directly.
+ */
+ pj_str_t type;
+ pj_str_t subtype;
+ pj_str_t text;
+
+ pj_cstr(&type, ast_sip_subscription_get_body_type(root));
+ pj_cstr(&subtype, ast_sip_subscription_get_body_subtype(root));
+ pj_cstr(&text, ast_str_buffer(root->body_text));
+
+ body = pjsip_msg_body_create(pool, &type, &subtype, &text);
+ root->body_changed = 0;
+ } else {
+ body = NULL;
+ }
+ } else {
+ body = generate_list_body(pool, root, force_full_state);
+ }
+
+ return body;
+}
+
+/*!
+ * \brief Shortcut method to create a Require: eventlist header
+ */
+static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
+{
+ pjsip_require_hdr *require;
+
+ require = pjsip_require_hdr_create(pool);
+ pj_strdup2(pool, &require->values[0], "eventlist");
+ require->count = 1;
+
+ return require;
+}
+
+/*!
+ * \brief Send a NOTIFY request to a subscriber
+ *
+ * \param sub_tree The subscription tree representing the subscription
+ * \param force_full_state If true, ignore resource list settings and send full resource list state.
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state)
{
pjsip_evsub *evsub = sub_tree->evsub;
pjsip_tx_data *tdata;
- struct ast_sip_body body = {
- .type = ast_sip_subscription_get_body_type(sub_tree->root),
- .subtype = ast_sip_subscription_get_body_subtype(sub_tree->root),
- };
- RAII_VAR(struct ast_str *, body_text, ast_str_create(64), ast_free);
-
- if (!body_text) {
- return -1;
- }
if (pjsip_evsub_notify(evsub, sub_tree->root->subscription_state,
NULL, NULL, &tdata) != PJ_SUCCESS) {
return -1;
}
- if (generate_notify_body(sub_tree->root, &body_text)) {
+ tdata->msg->body = generate_notify_body(tdata->pool, sub_tree->root, force_full_state);
+ if (!tdata->msg->body) {
pjsip_tx_data_dec_ref(tdata);
return -1;
}
- body.body_text = ast_str_buffer(body_text);
-
- if (ast_sip_add_body(tdata, &body)) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
+ if (sub_tree->is_list) {
+ pjsip_require_hdr *require = create_require_eventlist(tdata->pool);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
}
if (sip_subscription_send_request(sub_tree, tdata)) {
- ast_free(body_text);
- pjsip_tx_data_dec_ref(tdata);
return -1;
}
@@ -1576,19 +1982,20 @@
* bail out here instead of sending the batched NOTIFY.
*/
if (!sub_tree->send_scheduled_notify) {
+ ao2_cleanup(sub_tree);
return 0;
}
- send_notify(sub_tree);
+ send_notify(sub_tree, 0);
ao2_cleanup(sub_tree);
return 0;
}
static int sched_cb(const void *data)
{
- /* Why the #*(@% does the scheduler give us const data?! */
struct sip_subscription_tree *sub_tree = (struct sip_subscription_tree *) data;
+ /* We don't need to bump the refcount of sub_tree since we bumped it when scheduling this task */
ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree);
return 0;
}
@@ -1625,14 +2032,13 @@
if (sub->tree->notification_batch_interval) {
return schedule_notification(sub->tree);
} else {
- return send_notify(sub->tree);
+ return send_notify(sub->tree, 0);
}
}
void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
{
- pjsip_dialog *dlg = sub->tree->dlg;
- ast_copy_pj_str(buf, &dlg->local.info_str, size);
+ pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->uri, buf, size);
}
void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
@@ -1644,17 +2050,6 @@
const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub)
{
return sub->resource;
-}
-
-static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
-{
- pjsip_require_hdr *require;
-
- require = pjsip_require_hdr_create(pool);
- pj_strdup2(pool, &require->values[0], "eventlist");
- require->count = 1;
-
- return require;
}
static int sip_subscription_accept(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, int response)
@@ -2066,7 +2461,7 @@
if (generate_initial_notify(sub_tree->root)) {
pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
}
- send_notify(sub_tree);
+ send_notify(sub_tree, 1);
}
resource_tree_destroy(&tree);
@@ -2551,22 +2946,6 @@
return PJ_FALSE;
}
-static void shutdown_subscriptions(struct ast_sip_subscription *sub)
-{
- int i;
-
- if (AST_VECTOR_SIZE(&sub->children) > 0) {
- for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
- shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
- }
- return;
- }
-
- if (sub->handler->subscription_shutdown) {
- sub->handler->subscription_shutdown(sub);
- }
-}
-
static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
{
struct sip_subscription_tree *sub_tree;
@@ -2580,7 +2959,7 @@
return;
}
- shutdown_subscriptions(sub_tree->root);
+ ao2_cleanup(sub_tree);
pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
}
@@ -2605,17 +2984,27 @@
return;
}
+ /* If sending a NOTIFY to terminate a subscription, then pubsub_on_evsub_state()
+ * will be called when we send the NOTIFY, and that will result in dropping the
+ * refcount of sub_tree by one, and possibly destroying the sub_tree. We need to
+ * hold a reference to the sub_tree until this function returns so that we don't
+ * try to read from or write to freed memory by accident
+ */
+ ao2_ref(sub_tree, +1);
+
if (pjsip_evsub_get_state(evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
set_state_terminated(sub_tree->root);
}
- if (send_notify(sub_tree)) {
+ if (send_notify(sub_tree, 1)) {
*p_st_code = 500;
}
if (sub_tree->is_list) {
pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
}
+
+ ao2_ref(sub_tree, -1);
}
static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
@@ -2659,7 +3048,7 @@
struct sip_subscription_tree *sub_tree = userdata;
set_state_terminated(sub_tree->root);
- send_notify(sub_tree);
+ send_notify(sub_tree, 1);
ao2_cleanup(sub_tree);
return 0;
@@ -2955,8 +3344,8 @@
OPT_NOOP_T, 0, 0);
ast_sorcery_object_field_register(sorcery, "resource_list", "event", "",
OPT_CHAR_ARRAY_T, 1, CHARFLDSET(struct resource_list, event));
- ast_sorcery_object_field_register(sorcery, "resource_list", "full_state", "0",
- OPT_BOOL_T, 0, FLDSET(struct resource_list, full_state));
+ ast_sorcery_object_field_register(sorcery, "resource_list", "full_state", "no",
+ OPT_BOOL_T, 1, FLDSET(struct resource_list, full_state));
ast_sorcery_object_field_register(sorcery, "resource_list", "notification_batch_interval",
"0", OPT_UINT_T, 0, FLDSET(struct resource_list, notification_batch_interval));
ast_sorcery_object_field_register_custom(sorcery, "resource_list", "list_item",
Modified: team/group/rls/res/res_pjsip_xpidf_body_generator.c
URL: http://svnview.digium.com/svn/asterisk/team/group/rls/res/res_pjsip_xpidf_body_generator.c?view=diff&rev=420365&r1=420364&r2=420365
==============================================================================
--- team/group/rls/res/res_pjsip_xpidf_body_generator.c (original)
+++ team/group/rls/res/res_pjsip_xpidf_body_generator.c Thu Aug 7 13:58:04 2014
@@ -98,7 +98,6 @@
}
#define MAX_STRING_GROWTHS 5
-#define XML_PROLOG 39
static void xpidf_to_string(void *body, struct ast_str **str)
{
@@ -108,13 +107,13 @@
do {
size = pjxpidf_print(pres, ast_str_buffer(*str), ast_str_size(*str));
- if (size == XML_PROLOG) {
+ if (size == AST_PJSIP_XML_PROLOG_LEN) {
ast_str_make_space(str, ast_str_size(*str) * 2);
++growths;
}
- } while (size == XML_PROLOG && growths < MAX_STRING_GROWTHS);
+ } while (size == AST_PJSIP_XML_PROLOG_LEN && growths < MAX_STRING_GROWTHS);
- if (size == XML_PROLOG) {
+ if (size == AST_PJSIP_XML_PROLOG_LEN) {
ast_log(LOG_WARNING, "XPIDF body text too large\n");
return;
}
More information about the svn-commits
mailing list