[asterisk-commits] mmichelson: trunk r420384 - in /trunk: include/asterisk/ main/ res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Aug 7 14:26:36 CDT 2014


Author: mmichelson
Date: Thu Aug  7 14:26:32 2014
New Revision: 420384

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=420384
Log:
Add support for RFC 4662 resource list subscriptions.

This commit adds the ability for a user to configure
a resource list in pjsip.conf. Subscribing to this
list simultaneously subscribes the subscriber to all
resources listed. This has the potential to reduce
the amount of SIP traffic when loads of subscribers
on a system attempt to subscribe to each others' states.


Modified:
    trunk/include/asterisk/res_pjsip_presence_xml.h
    trunk/include/asterisk/res_pjsip_pubsub.h
    trunk/include/asterisk/strings.h
    trunk/main/strings.c
    trunk/res/res_pjsip_dialog_info_body_generator.c
    trunk/res/res_pjsip_exten_state.c
    trunk/res/res_pjsip_mwi.c
    trunk/res/res_pjsip_pidf_body_generator.c
    trunk/res/res_pjsip_pubsub.c
    trunk/res/res_pjsip_xpidf_body_generator.c

Modified: trunk/include/asterisk/res_pjsip_presence_xml.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_pjsip_presence_xml.h?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/include/asterisk/res_pjsip_presence_xml.h (original)
+++ trunk/include/asterisk/res_pjsip_presence_xml.h Thu Aug  7 14:26:32 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: trunk/include/asterisk/res_pjsip_pubsub.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_pjsip_pubsub.h?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/include/asterisk/res_pjsip_pubsub.h (original)
+++ trunk/include/asterisk/res_pjsip_pubsub.h Thu Aug  7 14:26:32 2014
@@ -240,23 +240,29 @@
 	 */
 	int (*new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource);
 	/*!
-	 * \brief The subscription is in need of a NOTIFY request.
-	 *
-	 * A reason of AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED is given immediately
-	 * after a SUBSCRIBE is accepted. This is a good opportunity for the notifier to
-	 * perform setup duties such as establishing Stasis subscriptions or adding
-	 * datastores to the subscription.
-	 *
-	 * A reason of AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED is given when the
-	 * subscriber has terminated the subscription. If there are any duties that the
-	 *
-	 *
-	 * \param sub The subscription to send the NOTIFY on.
-	 * \param reason The reason why the NOTIFY is being sent.
+	 * \brief Called when an inbound subscription has been accepted.
+	 *
+	 * This is a prime opportunity for notifiers to add any notifier-specific
+	 * data to the subscription (such as datastores) that it needs to.
+	 *
+	 * \note There is no need to send a NOTIFY request when this callback
+	 * is called
+	 *
+	 * \param sub The new subscription
 	 * \retval 0 Success
 	 * \retval -1 Failure
 	 */
-	int (*notify_required)(struct ast_sip_subscription *sub, enum ast_sip_subscription_notify_reason reason);
+	int (*subscription_established)(struct ast_sip_subscription *sub);
+	/*!
+	 * \brief Supply data needed to create a NOTIFY body.
+	 *
+	 * The returned data must be an ao2 object. The caller of this function
+	 * will be responsible for decrementing the refcount of the returned object
+	 *
+	 * \param sub The subscription
+	 * \return An ao2 object that can be used to create a NOTIFY body.
+	 */
+	void *(*get_notify_data)(struct ast_sip_subscription *sub);
 };
 
 struct ast_sip_subscriber {
@@ -343,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.
@@ -359,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: trunk/include/asterisk/strings.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/strings.h?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/include/asterisk/strings.h (original)
+++ trunk/include/asterisk/strings.h Thu Aug  7 14:26:32 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: trunk/main/strings.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/strings.c?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/main/strings.c (original)
+++ trunk/main/strings.c Thu Aug  7 14:26:32 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: trunk/res/res_pjsip_dialog_info_body_generator.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_dialog_info_body_generator.c?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/res/res_pjsip_dialog_info_body_generator.c (original)
+++ trunk/res/res_pjsip_dialog_info_body_generator.c Thu Aug  7 14:26:32 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: trunk/res/res_pjsip_exten_state.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_exten_state.c?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/res/res_pjsip_exten_state.c (original)
+++ trunk/res/res_pjsip_exten_state.c Thu Aug  7 14:26:32 2014
@@ -70,15 +70,23 @@
 
 static void subscription_shutdown(struct ast_sip_subscription *sub);
 static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource);
-static int notify_required(struct ast_sip_subscription *sub,
-		enum ast_sip_subscription_notify_reason reason);
+static int subscription_established(struct ast_sip_subscription *sub);
+static void *get_notify_data(struct ast_sip_subscription *sub);
 static void to_ami(struct ast_sip_subscription *sub,
 		   struct ast_str **buf);
 
 struct ast_sip_notifier presence_notifier = {
 	.default_accept = DEFAULT_PRESENCE_BODY,
 	.new_subscribe = new_subscribe,
-	.notify_required = notify_required,
+	.subscription_established = subscription_established,
+	.get_notify_data = get_notify_data,
+};
+
+struct ast_sip_notifier dialog_notifier = {
+	.default_accept = DEFAULT_DIALOG_BODY,
+	.new_subscribe = new_subscribe,
+	.subscription_established = subscription_established,
+	.get_notify_data = get_notify_data,
 };
 
 struct ast_sip_subscription_handler presence_handler = {
@@ -94,7 +102,7 @@
 	.accept = { DEFAULT_DIALOG_BODY, },
 	.subscription_shutdown = subscription_shutdown,
 	.to_ami = to_ami,
-	.notifier = &presence_notifier,
+	.notifier = &dialog_notifier,
 };
 
 static void exten_state_subscription_destructor(void *obj)
@@ -151,45 +159,6 @@
 	exten_state_sub->last_presence_state = AST_PRESENCE_NOT_SET;
 	exten_state_sub->user_agent = get_user_agent(sip_sub);
 	return exten_state_sub;
-}
-
-/*!
- * \internal
- * \brief Get device state information and send notification to the subscriber.
- */
-static void send_notify(struct exten_state_subscription *exten_state_sub)
-{
-	RAII_VAR(struct ao2_container*, info, NULL, ao2_cleanup);
-	char *subtype = NULL, *message = NULL;
-	struct ast_sip_exten_state_data exten_state_data = {
-		.exten = exten_state_sub->exten,
-		.presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
-							  exten_state_sub->exten, &subtype, &message),
-		.presence_subtype = subtype,
-		.presence_message = message,
-		.sub = exten_state_sub->sip_sub,
-		.user_agent = exten_state_sub->user_agent
-	};
-
-	ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub,
-			exten_state_data.local, sizeof(exten_state_data.local));
-	ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub,
-			exten_state_data.remote, sizeof(exten_state_data.remote));
-
-	if ((exten_state_data.exten_state = ast_extension_state_extended(
-		     NULL, exten_state_sub->context, exten_state_sub->exten, &info)) < 0) {
-
-		ast_log(LOG_WARNING, "Unable to get device hint/info for extension %s\n",
-			exten_state_sub->exten);
-		return;
-	}
-
-	exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
-			"exten_state", 1024, 1024);
-
-	exten_state_data.device_state_info = info;
-	ast_sip_subscription_notify(exten_state_sub->sip_sub, &exten_state_data, 0);
-	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data.pool);
 }
 
 struct notify_task_data {
@@ -231,11 +200,8 @@
 	task_data->exten_state_data.presence_subtype = ast_strdup(info->presence_subtype);
 	task_data->exten_state_data.presence_message = ast_strdup(info->presence_message);
 	task_data->exten_state_data.user_agent = ast_strdup(exten_state_sub->user_agent);
-	task_data->exten_state_data.device_state_info = info->device_state_info;
-
-	if (task_data->exten_state_data.device_state_info) {
-		ao2_ref(task_data->exten_state_data.device_state_info, +1);
-	}
+	task_data->exten_state_data.device_state_info = ao2_bump(info->device_state_info);
+	task_data->exten_state_data.sub = exten_state_sub->sip_sub;
 
 	ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub,
 			task_data->exten_state_data.local, sizeof(task_data->exten_state_data.local));
@@ -259,6 +225,9 @@
 	/* Pool allocation has to happen here so that we allocate within a PJLIB thread */
 	task_data->exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
 			"exten_state", 1024, 1024);
+	if (!task_data->exten_state_data.pool) {
+		return -1;
+	}
 
 	task_data->exten_state_data.sub = task_data->exten_state_sub->sip_sub;
 
@@ -366,7 +335,7 @@
 	return 200;
 }
 
-static int initial_subscribe(struct ast_sip_subscription *sip_sub)
+static int subscription_established(struct ast_sip_subscription *sip_sub)
 {
 	struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);
 	const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
@@ -403,33 +372,77 @@
 		return -1;
 	}
 
-	send_notify(exten_state_sub);
 	ao2_cleanup(exten_state_sub);
 	return 0;
 }
 
-static int notify_required(struct ast_sip_subscription *sub,
-		enum ast_sip_subscription_notify_reason reason)
+static void exten_state_data_destructor(void *obj)
+{
+	struct ast_sip_exten_state_data *exten_state_data = obj;
+
+	ao2_cleanup(exten_state_data->device_state_info);
+	ast_free(exten_state_data->presence_subtype);
+	ast_free(exten_state_data->presence_message);
+	if (exten_state_data->pool) {
+		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data->pool);
+	}
+}
+
+static struct ast_sip_exten_state_data *exten_state_data_alloc(struct ast_sip_subscription *sip_sub,
+		struct exten_state_subscription *exten_state_sub)
+{
+	struct ast_sip_exten_state_data *exten_state_data;
+	char *subtype = NULL;
+	char *message = NULL;
+
+	exten_state_data = ao2_alloc(sizeof(*exten_state_data), exten_state_data_destructor);
+	if (!exten_state_data) {
+		return NULL;
+	}
+
+	exten_state_data->exten = exten_state_sub->exten;
+	if ((exten_state_data->presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
+			exten_state_sub->exten, &subtype, &message)) == -1) {
+		ao2_cleanup(exten_state_data);
+		return NULL;
+	}
+	exten_state_data->presence_subtype = subtype;
+	exten_state_data->presence_message = message;
+	exten_state_data->user_agent = exten_state_sub->user_agent;
+	ast_sip_subscription_get_local_uri(sip_sub, exten_state_data->local,
+			sizeof(exten_state_data->local));
+	ast_sip_subscription_get_remote_uri(sip_sub, exten_state_data->remote,
+			sizeof(exten_state_data->remote));
+	exten_state_data->sub = sip_sub;
+
+	exten_state_data->exten_state = ast_extension_state_extended(
+			NULL, exten_state_sub->context, exten_state_sub->exten,
+			&exten_state_data->device_state_info);
+	if (exten_state_data->exten_state < 0) {
+		ao2_cleanup(exten_state_data);
+		return NULL;
+	}
+
+	exten_state_data->pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
+			"exten_state", 1024, 1024);
+	if (!exten_state_data->pool) {
+		ao2_cleanup(exten_state_data);
+		return NULL;
+	}
+
+	return exten_state_data;
+}
+
+static void *get_notify_data(struct ast_sip_subscription *sub)
 {
 	struct exten_state_subscription *exten_state_sub;
 
-	switch (reason) {
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED:
-		return initial_subscribe(sub);
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED:
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED:
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER:
-		exten_state_sub = get_exten_state_sub(sub);
-
-		if (!exten_state_sub) {
-			return -1;
-		}
-
-		send_notify(exten_state_sub);
-		break;
-	}
-
-	return 0;
+	exten_state_sub = get_exten_state_sub(sub);
+	if (!exten_state_sub) {
+		return NULL;
+	}
+
+	return exten_state_data_alloc(sub, exten_state_sub);
 }
 
 static void to_ami(struct ast_sip_subscription *sub,

Modified: trunk/res/res_pjsip_mwi.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_mwi.c?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/res/res_pjsip_mwi.c (original)
+++ trunk/res/res_pjsip_mwi.c Thu Aug  7 14:26:32 2014
@@ -48,17 +48,20 @@
 #define MWI_TYPE "application"
 #define MWI_SUBTYPE "simple-message-summary"
 
+#define MWI_DATASTORE "MWI datastore"
+
 static void mwi_subscription_shutdown(struct ast_sip_subscription *sub);
 static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf);
 static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
 		const char *resource);
-static int mwi_notify_required(struct ast_sip_subscription *sip_sub,
-		enum ast_sip_subscription_notify_reason reason);
+static int mwi_subscription_established(struct ast_sip_subscription *sub);
+static void *mwi_get_notify_data(struct ast_sip_subscription *sub);
 
 static struct ast_sip_notifier mwi_notifier = {
 	.default_accept = MWI_TYPE"/"MWI_SUBTYPE,
 	.new_subscribe = mwi_new_subscribe,
-	.notify_required = mwi_notify_required,
+	.subscription_established = mwi_subscription_established,
+	.get_notify_data = mwi_get_notify_data,
 };
 
 static struct ast_sip_subscription_handler mwi_handler = {
@@ -457,7 +460,7 @@
 {
 	struct mwi_subscription *mwi_sub;
 	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup);
+			ast_sip_subscription_get_datastore(sub, MWI_DATASTORE), ao2_cleanup);
 
 	if (!mwi_datastore) {
 		return;
@@ -473,7 +476,7 @@
 {
 	RAII_VAR(struct ast_datastore *, mwi_datastore, NULL, ao2_cleanup);
 
-	mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, "MWI datastore");
+	mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, MWI_DATASTORE);
 	if (!mwi_datastore) {
 		return -1;
 	}
@@ -676,7 +679,7 @@
 	return 200;
 }
 
-static int mwi_initial_subscription(struct ast_sip_subscription *sip_sub)
+static int mwi_subscription_established(struct ast_sip_subscription *sip_sub)
 {
 	const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
 	struct mwi_subscription *sub;
@@ -694,39 +697,32 @@
 		return -1;
 	}
 
-	send_mwi_notify(sub);
-
 	ao2_cleanup(sub);
 	ao2_cleanup(endpoint);
 	return 0;
 }
 
-static int mwi_notify_required(struct ast_sip_subscription *sip_sub,
-		enum ast_sip_subscription_notify_reason reason)
-{
+static void *mwi_get_notify_data(struct ast_sip_subscription *sub)
+{
+	struct ast_sip_message_accumulator *counter;
 	struct mwi_subscription *mwi_sub;
 	struct ast_datastore *mwi_datastore;
 
-	switch (reason) {
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED:
-		return mwi_initial_subscription(sip_sub);
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED:
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED:
-	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER:
-		mwi_datastore = ast_sip_subscription_get_datastore(sip_sub, "MWI datastore");
-
-		if (!mwi_datastore) {
-			return -1;
-		}
-
-		mwi_sub = mwi_datastore->data;
-
-		send_mwi_notify(mwi_sub);
+	mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE);
+	if (!mwi_datastore) {
+		return NULL;
+	}
+	mwi_sub = mwi_datastore->data;
+
+	counter = ao2_alloc(sizeof(*counter), NULL);
+	if (!counter) {
 		ao2_cleanup(mwi_datastore);
-		break;
-	}
-
-	return 0;
+		return NULL;
+	}
+
+	ao2_callback(mwi_sub->stasis_subs, OBJ_NODATA, get_message_count, counter);
+	ao2_cleanup(mwi_datastore);
+	return counter;
 }
 
 static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs,
@@ -753,7 +749,7 @@
 {
 	struct mwi_subscription *mwi_sub;
 	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup);
+			ast_sip_subscription_get_datastore(sub, MWI_DATASTORE), ao2_cleanup);
 
 	if (!mwi_datastore) {
 		return;

Modified: trunk/res/res_pjsip_pidf_body_generator.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_pidf_body_generator.c?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/res/res_pjsip_pidf_body_generator.c (original)
+++ trunk/res/res_pjsip_pidf_body_generator.c Thu Aug  7 14:26:32 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: trunk/res/res_pjsip_pubsub.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_pubsub.c?view=diff&rev=420384&r1=420383&r2=420384
==============================================================================
--- trunk/res/res_pjsip_pubsub.c (original)
+++ trunk/res/res_pjsip_pubsub.c Thu Aug  7 14:26:32 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">
@@ -72,6 +73,20 @@
                         </para>
 		</description>
 	</manager>
+	<manager name="PJSIPShowResourceLists" language="en_US">
+		<synopsis>
+			Displays settings for configured resource lists.
+		</synopsis>
+		<syntax />
+		<description>
+			<para>
+			Provides a listing of all resource lists.  An event <literal>ResourceListDetail</literal>
+			is issued for each resource list object.  Once all detail events are completed a
+			<literal>ResourceListDetailComplete</literal> event is issued.
+                        </para>
+		</description>
+	</manager>
+
 	<configInfo name="res_pjsip_pubsub" language="en_US">
 		<synopsis>Module that implements publish and subscribe support.</synopsis>
 		<configFile name="pjsip.conf">
@@ -108,6 +123,66 @@
 					<synopsis>The time at which the subscription expires</synopsis>
 				</configOption>
 			</configObject>
+			<configObject name="resource_list">
+				<synopsis>Resource list configuration parameters.</synopsis>
+				<configOption name="type">
+					<synopsis>Must be of type 'resource_list'</synopsis>
+				</configOption>
+				<configOption name="event">
+					<synopsis>The SIP event package that the list resource belong to.</synopsis>
+					<description><para>
+						The SIP event package describes the types of resources that Asterisk reports
+						the state of.
+					</para>
+						<enumlist>
+							<enum name="presence"><para>
+								Device state and presence reporting.
+							</para></enum>
+							<enum name="message-summary"><para>
+								Message-waiting indication (MWI) reporting.
+							</para></enum>
+						</enumlist>
+					</description>
+				</configOption>
+				<configOption name="list_item">
+					<synopsis>The name of a resource to report state on</synopsis>
+					<description>
+						<para>In general Asterisk looks up list items in the following way:</para>
+						<para>1. Check if the list item refers to another configured resource list.</para>
+						<para>2. Pass the name of the resource off to event-package-specific handlers
+						   to find the specified resource.</para>
+						<para>The second part means that the way the list item is specified depends
+						on what type of list this is. For instance, if you have the <replaceable>event</replaceable>
+						set to <literal>presence</literal>, then list items should be in the form of
+						dialplan_extension at dialplan_context. For <literal>message-summary</literal> mailbox
+						names should be listed.</para>
+					</description>
+				</configOption>
+				<configOption name="full_state" default="no">
+					<synopsis>Indicates if the entire list's state should be sent out.</synopsis>
+					<description>
+						<para>If this option is enabled, and a resource changes state, then Asterisk will construct
+						a notification that contains the state of all resources in the list. If the option is
+						disabled, Asterisk will construct a notification that only contains the states of
+						resources that have changed.</para>
+						<note>
+							<para>Even with this option disabled, there are certain situations where Asterisk is forced
+							to send a notification with the states of all resources in the list. When a subscriber
+							renews or terminates its subscription to the list, Asterisk MUST send a full state
+							notification.</para>
+						</note>
+					</description>
+				</configOption>
+				<configOption name="notification_batch_interval" default="0">
+					<synopsis>Time Asterisk should wait, in milliseconds, before sending notifications.</synopsis>
+					<description>
+						<para>When a resource's state changes, it may be desired to wait a certain amount before Asterisk
+						sends a notification to subscribers. This allows for other state changes to accumulate, so that
+						Asterisk can communicate multiple state changes in a single notification instead of rapidly sending
+						many notifications.</para>
+					</description>
+				</configOption>
+			</configObject>
 			<configObject name="inbound-publication">
 				<synopsis>The configuration for inbound publications</synopsis>
 				<configOption name="endpoint" default="">
@@ -143,6 +218,12 @@
 /*! \brief Default expiration time for PUBLISH if one is not specified */
 #define DEFAULT_PUBLISH_EXPIRES 3600
 
+/*! \brief Number of buckets for subscription datastore */
+#define DATASTORE_BUCKETS 53
+
+/*! \brief Default expiration for subscriptions */
+#define DEFAULT_EXPIRES 3600
+
 /*! \brief Defined method for PUBLISH */
 const pjsip_method pjsip_publish_method =
 {
@@ -203,6 +284,26 @@
 	 * an Expires header set to 0 and likely no body.
 	 */
 	SIP_PUBLISH_REMOVE,
+};
+
+/*!
+ * \brief A vector of strings commonly used throughout this module
+ */
+AST_VECTOR(resources, const char *);
+
+/*!
+ * \brief Resource list configuration item
+ */
+struct resource_list {
+	SORCERY_OBJECT(details);
+	/*! SIP event package the list uses. */
+	char event[32];
+	/*! Strings representing resources in the list. */
+	struct resources items;
+	/*! Indicates if Asterisk sends full or partial state on notifications. */
+	unsigned int full_state;
+	/*! Time, in milliseconds Asterisk waits before sending a batched notification.*/
+	unsigned int notification_batch_interval;
 };
 
 /*!
@@ -264,83 +365,72 @@
 };
 
 /*!
- * \brief Real subscription details
- *
- * A real subscription is one that has a direct link to a
- * PJSIP subscription and dialog.
- */
-struct ast_sip_real_subscription {
+ * \brief A tree of SIP subscriptions
+ *
+ * Because of the ability to subscribe to resource lists, a SIP
+ * subscription can result in a tree of subscriptions being created.
+ * This structure represents the information relevant to the subscription
+ * as a whole, to include the underlying PJSIP structure for the
+ * subscription.
+ */
+struct sip_subscription_tree {
+	/*! The endpoint with which the subscription is communicating */
+	struct ast_sip_endpoint *endpoint;
+	/*! Serializer on which to place operations for this subscription */
+	struct ast_taskprocessor *serializer;
+	/*! The role for this subscription */
+	enum ast_sip_subscription_role role;
+	/*! Persistence information */
+	struct subscription_persistence *persistence;
 	/*! The underlying PJSIP event subscription structure */
 	pjsip_evsub *evsub;
 	/*! The underlying PJSIP dialog */
 	pjsip_dialog *dlg;
+	/*! Interval to use for batching notifications */
+	unsigned int notification_batch_interval;
+	/*! Scheduler ID for batched notification */
+	int notify_sched_id;
+	/*! Indicator if scheduled batched notification should be sent */
+	unsigned int send_scheduled_notify;
+	/*! The root of the subscription tree */
+	struct ast_sip_subscription *root;
+	/*! Is this subscription to a list? */
+	int is_list;
+	/*! Next item in the list */
+	AST_LIST_ENTRY(sip_subscription_tree) next;
 };
 
 /*!
- * \brief Virtual subscription details
- *
- * A virtual subscription is one that does not have a direct
- * link to a PJSIP subscription. Instead, it is a descendent
- * of an ast_sip_subscription. Following the ancestry will
- * eventually lead to a real subscription.
- */
-struct ast_sip_virtual_subscription {
-	struct ast_sip_subscription *parent;
-};
-
-/*!
- * \brief Discriminator between real and virtual subscriptions
- */
-enum sip_subscription_type {
-	/*!
-	 * \brief a "real" subscription.
-	 *
-	 * Real subscriptions are at the root of a tree of subscriptions.
-	 * A real subscription has a corresponding SIP subscription in the
-	 * PJSIP stack.
-	 */
-	SIP_SUBSCRIPTION_REAL,
-	/*!
-	 * \brief a "virtual" subscription.
-	 *
-	 * Virtual subscriptions are the descendents of real subscriptions
-	 * in a tree of subscriptions. Virtual subscriptions do not have
-	 * a corresponding SIP subscription in the PJSIP stack. Instead,
-	 * when a state change happens on a virtual subscription, the
-	 * state change is indicated to the virtual subscription's parent.
-	 */
-	SIP_SUBSCRIPTION_VIRTUAL,
-};
-
-/*!
- * \brief Structure representing a SIP subscription
+ * \brief Structure representing a "virtual" SIP subscription.
+ *
+ * This structure serves a dual purpose. Structurally, it is
+ * the constructed tree of subscriptions based on the resources
+ * being subscribed to. API-wise, this serves as the handle that
+ * subscription handlers use in order to interact with the pubsub API.
  */
 struct ast_sip_subscription {
 	/*! Subscription datastores set up by handlers */
 	struct ao2_container *datastores;
-	/*! The endpoint with which the subscription is communicating */
-	struct ast_sip_endpoint *endpoint;
-	/*! Serializer on which to place operations for this subscription */
-	struct ast_taskprocessor *serializer;
 	/*! The handler for this subscription */
 	const struct ast_sip_subscription_handler *handler;
-	/*! The role for this subscription */
-	enum ast_sip_subscription_role role;
-	/*! Indicator of real or virtual subscription */
-	enum sip_subscription_type type;
-	/*! Real and virtual components of the subscription */
-	union {
-		struct ast_sip_real_subscription real;
-		struct ast_sip_virtual_subscription virtual;
-	} reality;
+	/*! Pointer to the base of the tree */
+	struct sip_subscription_tree *tree;
 	/*! Body generaator for NOTIFYs */
 	struct ast_sip_pubsub_body_generator *body_generator;
-	/*! Persistence information */
-	struct subscription_persistence *persistence;
-	/*! Next item in the list */
-	AST_LIST_ENTRY(ast_sip_subscription) next;
-	/*! List of child subscriptions */
-	AST_LIST_HEAD_NOLOCK(,ast_sip_subscription) children;
+	/*! Vector of child subscriptions */
+	AST_VECTOR(, struct ast_sip_subscription *) children;
+	/*! Saved NOTIFY body text for this subscription */
+	struct ast_str *body_text;
+	/*! Indicator that the body text has changed since the last notification */
+	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];
 };
@@ -362,20 +452,26 @@
 	[AST_SIP_NOTIFIER] = "Notifier"
 };
 
-AST_RWLIST_HEAD_STATIC(subscriptions, ast_sip_subscription);
+AST_RWLIST_HEAD_STATIC(subscriptions, sip_subscription_tree);
 
 AST_RWLIST_HEAD_STATIC(body_generators, ast_sip_pubsub_body_generator);
 AST_RWLIST_HEAD_STATIC(body_supplements, ast_sip_pubsub_body_supplement);
 
-static pjsip_evsub *sip_subscription_get_evsub(const struct ast_sip_subscription *sub)
-{
-	return sub->reality.real.evsub;
-}
-
-static pjsip_dialog *sip_subscription_get_dlg(const struct ast_sip_subscription *sub)
-{
-	return sub->reality.real.dlg;
-}
+static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event);
+static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
+		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
+static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
+		pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
+static void pubsub_on_client_refresh(pjsip_evsub *sub);
+static void pubsub_on_server_timeout(pjsip_evsub *sub);
+ 
+static pjsip_evsub_user pubsub_cb = {
+	.on_evsub_state = pubsub_on_evsub_state,
+	.on_rx_refresh = pubsub_on_rx_refresh,
+	.on_rx_notify = pubsub_on_rx_notify,
+	.on_client_refresh = pubsub_on_client_refresh,
+	.on_server_timeout = pubsub_on_server_timeout,
+};
 
 /*! \brief Destructor for publication resource */
 static void publication_resource_destroy(void *obj)
@@ -408,7 +504,7 @@
 }
 
 /*! \brief Function which creates initial persistence information of a subscription in sorcery */
-static struct subscription_persistence *subscription_persistence_create(struct ast_sip_subscription *sub)
+static struct subscription_persistence *subscription_persistence_create(struct sip_subscription_tree *sub_tree)
 {
 	char tag[PJ_GUID_STRING_LENGTH + 1];
 
@@ -418,13 +514,13 @@
 	struct subscription_persistence *persistence = ast_sorcery_alloc(ast_sip_get_sorcery(),
 		"subscription_persistence", NULL);
 
-	pjsip_dialog *dlg = sip_subscription_get_dlg(sub);
+	pjsip_dialog *dlg = sub_tree->dlg;
 
 	if (!persistence) {
 		return NULL;
 	}
 
-	persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub->endpoint));
+	persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub_tree->endpoint));
 	ast_copy_pj_str(tag, &dlg->local.info->tag, sizeof(tag));
 	persistence->tag = ast_strdup(tag);
 
@@ -433,47 +529,49 @@
 }
 
 /*! \brief Function which updates persistence information of a subscription in sorcery */
-static void subscription_persistence_update(struct ast_sip_subscription *sub,
+static void subscription_persistence_update(struct sip_subscription_tree *sub_tree,
 	pjsip_rx_data *rdata)
 {
 	pjsip_dialog *dlg;
 
-	if (!sub->persistence) {
+	if (!sub_tree->persistence) {
 		return;
 	}
 
-	dlg = sip_subscription_get_dlg(sub);
-	sub->persistence->cseq = dlg->local.cseq;
+	dlg = sub_tree->dlg;
+	sub_tree->persistence->cseq = dlg->local.cseq;
 
 	if (rdata) {
 		int expires;
 		pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
 
 		expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
-		sub->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
-
-		ast_copy_string(sub->persistence->packet, rdata->pkt_info.packet, sizeof(sub->persistence->packet));
-		ast_copy_string(sub->persistence->src_name, rdata->pkt_info.src_name, sizeof(sub->persistence->src_name));
-		sub->persistence->src_port = rdata->pkt_info.src_port;
-		ast_copy_string(sub->persistence->transport_key, rdata->tp_info.transport->type_name,
-			sizeof(sub->persistence->transport_key));
-		ast_copy_pj_str(sub->persistence->local_name, &rdata->tp_info.transport->local_name.host,
-			sizeof(sub->persistence->local_name));
-		sub->persistence->local_port = rdata->tp_info.transport->local_name.port;
-	}
-
-	ast_sorcery_update(ast_sip_get_sorcery(), sub->persistence);
+		sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
+
+		ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
+				sizeof(sub_tree->persistence->packet));
+		ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
+				sizeof(sub_tree->persistence->src_name));
+		sub_tree->persistence->src_port = rdata->pkt_info.src_port;
+		ast_copy_string(sub_tree->persistence->transport_key, rdata->tp_info.transport->type_name,
+			sizeof(sub_tree->persistence->transport_key));
+		ast_copy_pj_str(sub_tree->persistence->local_name, &rdata->tp_info.transport->local_name.host,
+			sizeof(sub_tree->persistence->local_name));
+		sub_tree->persistence->local_port = rdata->tp_info.transport->local_name.port;
+	}
+
+	ast_sorcery_update(ast_sip_get_sorcery(), sub_tree->persistence);
 }
 
 /*! \brief Function which removes persistence of a subscription from sorcery */
-static void subscription_persistence_remove(struct ast_sip_subscription *sub)
-{
-	if (!sub->persistence) {
+static void subscription_persistence_remove(struct sip_subscription_tree *sub_tree)
+{
+	if (!sub_tree->persistence) {
 		return;
 	}
 
-	ast_sorcery_delete(ast_sip_get_sorcery(), sub->persistence);
-	ao2_ref(sub->persistence, -1);
+	ast_sorcery_delete(ast_sip_get_sorcery(), sub_tree->persistence);
+	ao2_ref(sub_tree->persistence, -1);
 }
 
 
@@ -503,23 +601,62 @@
 	return handler;
 }
 
+/*!
+ * \brief Accept headers that are exceptions to the rule
+ *
+ * Typically, when a SUBSCRIBE arrives, we attempt to find a
+ * body generator that matches one of the Accept headers in
+ * the request. When subscribing to a single resource, this works
+ * great. However, when subscribing to a list, things work
+ * differently. Most Accept header values are fine, but there
+ * are a couple that are endemic to resource lists that need
+ * to be ignored when searching for a body generator to use
+ * for the individual resources of the subscription.
+ */
+const char *accept_exceptions[] =  {
+	"multipart/related",
+	"application/rlmi+xml",
+};
+
+/*!
+ * \brief Is the Accept header from the SUBSCRIBE in the list of exceptions?
+ *
+ * \retval 1 This Accept header value is an exception to the rule.
+ * \retval 0 This Accept header is not an exception to the rule.
+ */
+static int exceptional_accept(const pj_str_t *accept)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(accept_exceptions); ++i) {
+		if (!pj_strcmp2(accept, accept_exceptions[i])) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 /*! \brief Retrieve a body generator using the Accept header of an rdata message */
 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;
-
-	accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, rdata->msg_info.msg->hdr.next);
-	if (accept_header) {
+	size_t num_accept_headers = 0;
+
+	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) {
-			ast_copy_pj_str(accept[i], &accept_header->values[i], sizeof(accept[i]));
+			if (!exceptional_accept(&accept_header->values[i])) {
+				ast_copy_pj_str(accept[num_accept_headers], &accept_header->values[i], sizeof(accept[num_accept_headers]));
+				++num_accept_headers;
+			}
 		}
-		num_accept_headers = accept_header->count;
-	} 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.
 		 */
@@ -530,218 +667,290 @@
 	return find_body_generator(accept, num_accept_headers);
 }
 
-static struct ast_sip_subscription *notifier_create_subscription(const struct ast_sip_subscription_handler *handler,

[... 2863 lines stripped ...]



More information about the asterisk-commits mailing list