[asterisk-commits] mmichelson: branch mmichelson/rls-rlmi r420059 - in /team/mmichelson/rls-rlmi...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Aug 5 13:57:11 CDT 2014


Author: mmichelson
Date: Tue Aug  5 13:57:06 2014
New Revision: 420059

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=420059
Log:
Apply changes from the previous rls-rlmi branch.

This is the changeset that is on review board at /r/3741.
Next is to address the review comments.


Modified:
    team/mmichelson/rls-rlmi/include/asterisk/res_pjsip_pubsub.h
    team/mmichelson/rls-rlmi/res/res_pjsip_pubsub.c

Modified: team/mmichelson/rls-rlmi/include/asterisk/res_pjsip_pubsub.h
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/rls-rlmi/include/asterisk/res_pjsip_pubsub.h?view=diff&rev=420059&r1=420058&r2=420059
==============================================================================
--- team/mmichelson/rls-rlmi/include/asterisk/res_pjsip_pubsub.h (original)
+++ team/mmichelson/rls-rlmi/include/asterisk/res_pjsip_pubsub.h Tue Aug  5 13:57:06 2014
@@ -332,10 +332,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.
@@ -348,7 +347,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/mmichelson/rls-rlmi/res/res_pjsip_pubsub.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/rls-rlmi/res/res_pjsip_pubsub.c?view=diff&rev=420059&r1=420058&r2=420059
==============================================================================
--- team/mmichelson/rls-rlmi/res/res_pjsip_pubsub.c (original)
+++ team/mmichelson/rls-rlmi/res/res_pjsip_pubsub.c Tue Aug  5 13:57:06 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">
@@ -409,6 +410,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];
 };
@@ -593,21 +600,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.
 		 */
@@ -625,6 +633,7 @@
  */
 struct tree_node {
 	AST_VECTOR(, struct tree_node *) children;
+	unsigned int full_state;
 	char resource[0];
 };
 
@@ -664,10 +673,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;
 
@@ -681,6 +691,7 @@
 		ast_free(node);
 		return NULL;
 	}
+	node->full_state = full_state;
 
 	if (visited) {
 		AST_VECTOR_APPEND(visited, resource);
@@ -772,7 +783,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);
@@ -787,7 +798,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;
@@ -870,7 +881,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;
 		}
@@ -882,7 +893,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;
 	}
@@ -968,7 +979,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);
 
 	ao2_cleanup(sub->datastores);
 }
@@ -977,6 +988,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) {
@@ -995,6 +1007,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;
@@ -1024,6 +1041,7 @@
 		return NULL;
 	}
 
+	sub->full_state = current->full_state;
 	sub->body_generator = generator;
 
 	for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
@@ -1045,6 +1063,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;
@@ -1055,6 +1090,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);
 	remove_subscription(sub_tree);
@@ -1087,6 +1125,7 @@
 	}
 
 	sub_tree->endpoint = ao2_bump(endpoint);
+	sub_tree->notify_sched_id = -1;
 
 	add_subscription(sub_tree);
 	return sub_tree;
@@ -1452,65 +1491,432 @@
 	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.
+/*!
+ * \param 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.
+ *
+ * This is currently used for boundaries and Content-ID
+ * headers for multipart bodies. This is used instead of a UUID
+ * for two main reasons:
+ *
+ * 1) Since it is possible to generate many Content-IDs in a
+ * single multipart body, this is quicker than creating UUIDs.
+ * 2) Since multipart bodies tend to be large, using a smaller
+ * random string than a UUID can reduce the size of the generated
+ * bodies.
+ *
+ * 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
+ */
+static char *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;
+}
+
+/*!
+ * \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);
+
+	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>",
+			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;
+}
+
+/*!
+ * \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;
+	pj_str_t rlmi_text;
+	pjsip_multipart_part *rlmi_part;
+	char version_str[32];
+	char uri[PJSIP_MAX_URL_SIZE];
+	char rlmi_str[2048] = { 0,};
+	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);
+	}
+
+	pj_xml_print(rlmi, rlmi_str, sizeof(rlmi_str) - 1, PJ_TRUE);
+	pj_cstr(&rlmi_text, rlmi_str);
+
+	rlmi_part = pjsip_multipart_create_part(pool);
+	rlmi_part->body = pjsip_msg_body_create(pool, &rlmi_type, &rlmi_subtype, &rlmi_text);
+
+	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, 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;
+
+	AST_VECTOR_INIT(&body_parts, AST_VECTOR_SIZE(&sub->children));
+
+	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);
+	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_ptr);
-
-	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)) {
-		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 (sip_subscription_send_request(sub_tree, tdata)) {
-		ast_free(body_text);
 		pjsip_tx_data_dec_ref(tdata);
 		return -1;
 	}
@@ -1530,19 +1936,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;
 }
@@ -1579,14 +1986,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)
@@ -1598,17 +2004,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)
@@ -2020,7 +2415,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);
@@ -2460,22 +2855,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;
@@ -2489,7 +2868,7 @@
 		return;
 	}
 
-	shutdown_subscriptions(sub_tree->root);
+	ao2_cleanup(sub_tree);
 
 	pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
 }
@@ -2518,7 +2897,7 @@
 		set_state_terminated(sub_tree->root);
 	}
 
-	if (send_notify(sub_tree)) {
+	if (send_notify(sub_tree, 1)) {
 		*p_st_code = 500;
 	}
 
@@ -2568,7 +2947,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;
@@ -2864,8 +3243,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",




More information about the asterisk-commits mailing list