[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(&current->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