[Asterisk-code-review] res_pjsip_pubsub: Add new pubsub module capabilities. (asterisk[master])

N A asteriskteam at digium.com
Mon Apr 3 09:02:56 CDT 2023


N A has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/20031 )


Change subject: res_pjsip_pubsub: Add new pubsub module capabilities.
......................................................................

res_pjsip_pubsub: Add new pubsub module capabilities.

The existing res_pjsip_pubsub APIs are somewhat limited in
what they can do. This adds a few API extensions that make
it possible for PJSIP pubsub modules to implement richer
features than is currently possible.

* Allow pubsub modules to get a handle to pjsip_rx_data on subscription
* Allow pubsub modules to run a callback when a subscription is renewed
* Allow pubsub modules to run a callback for outgoing NOTIFYs, with
  a handle to the tdata, so that modules can append their own headers
  to the NOTIFYs

This change does not add any features directly, but makes possible
several new features that will be added in future changes.

ASTERISK-30485 #close

Master-Only: True
Change-Id: I5c54d32223eedab7460ca28616ed50ba4ce1341f
---
M include/asterisk/res_pjsip_pubsub.h
M res/res_pjsip_pubsub.c
2 files changed, 139 insertions(+), 36 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/31/20031/1

diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index aca1141..ac4bda8 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -269,6 +269,18 @@
 	 */
 	int (*new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource);
 	/*!
+	 * \brief Same as new_subscribe, but also pass a handle to the pjsip_rx_data
+	 *
+	 * \note If this callback exists, it will be executed, otherwise new_subscribe will be.
+	 *       Only use this if you need the rdata. Otherwise, use new_subscribe.
+	 *
+	 * \param endpoint The endpoint from which we received the SUBSCRIBE
+	 * \param resource The name of the resource to which the subscription is being made
+	 * \param rdata The pjsip_rx_data for incoming subscription
+	 * \return The response code to send to the SUBSCRIBE.
+	 */
+	int (*new_subscribe_with_rdata)(struct ast_sip_endpoint *endpoint, const char *resource, pjsip_rx_data *rdata);
+	/*!
 	 * \brief Called when an inbound subscription has been accepted.
 	 *
 	 * This is a prime opportunity for notifiers to add any notifier-specific
@@ -283,6 +295,25 @@
 	 */
 	int (*subscription_established)(struct ast_sip_subscription *sub);
 	/*!
+	 * \brief Called when a SUBSCRIBE arrives for an already active subscription.
+	 *
+	 * \param sub The existing subscription
+	 * \retval 0 Success
+	 * \retval -1 Failure
+	 */
+	int (*refresh_subscribe)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata);
+	/*!
+	 * \brief Optional callback to execute before sending outgoing NOTIFY responses.
+	 *        Because res_pjsip_pubsub creates the tdata internally, this allows modules
+	 *        to access the tdata if needed, e.g. to add custom headers.
+	 *
+	 * \param sub The existing subscription
+	 * \param tdata The pjsip_tx_data to use for the outgoing NOTIFY
+	 * \retval 0 Success
+	 * \retval -1 Failure
+	 */
+	int (*tdata_callback)(struct ast_sip_subscription *sub, pjsip_tx_data *tdata);
+	/*!
 	 * \brief Supply data needed to create a NOTIFY body.
 	 *
 	 * The returned data must be an ao2 object. The caller of this function
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 6ddb2fd..c8923d6 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -807,6 +807,30 @@
 	return handler;
 }
 
+/*! \brief Retrieve a handler using the Event header of a tdata message */
+static struct ast_sip_subscription_handler *subscription_get_handler_from_tdata(pjsip_tx_data *tdata, const char *endpoint)
+{
+	pjsip_event_hdr *event_header;
+	char event[32];
+	struct ast_sip_subscription_handler *handler;
+
+	event_header = pjsip_msg_find_hdr_by_name(tdata->msg, &str_event_name, tdata->msg->hdr.next);
+	if (!event_header) {
+		ast_log(LOG_WARNING, "Incoming SUBSCRIBE request from %s with no Event header\n",
+			endpoint ? endpoint : "Unknown");
+		return NULL;
+	}
+	ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
+
+	handler = find_sub_handler_for_event_name(event);
+	if (!handler) {
+		ast_log(LOG_WARNING, "No registered subscribe handler for event %s from %s\n", event,
+			endpoint ? endpoint : "Unknown");
+	}
+
+	return handler;
+}
+
 /*!
  * \brief Accept headers that are exceptions to the rule
  *
@@ -1018,6 +1042,8 @@
 	return 0;
 }
 
+#define NEW_SUBSCRIBE(notifier, endpoint, resource, rdata) notifier->new_subscribe_with_rdata ? notifier->new_subscribe_with_rdata(endpoint, resource, rdata) : notifier->new_subscribe(endpoint, resource)
+
 /*!
  * \brief Build child nodes for a given parent.
  *
@@ -1040,7 +1066,7 @@
  * \param visited The resources that have already been visited.
  */
 static void build_node_children(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
-		struct resource_list *list, struct tree_node *parent, struct resources *visited)
+		struct resource_list *list, struct tree_node *parent, struct resources *visited, pjsip_rx_data *rdata)
 {
 	int i;
 
@@ -1056,7 +1082,7 @@
 
 		child_list = retrieve_resource_list(resource, list->event);
 		if (!child_list) {
-			int resp = handler->notifier->new_subscribe(endpoint, resource);
+			int resp = NEW_SUBSCRIBE(handler->notifier, endpoint, resource, rdata);
 			if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
 				char display_name[AST_MAX_EXTENSION] = "";
 				if (list->resource_display_name && handler->notifier->get_resource_display_name) {
@@ -1085,7 +1111,7 @@
 				ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
 				continue;
 			}
-			build_node_children(endpoint, handler, child_list, current, visited);
+			build_node_children(endpoint, handler, child_list, current, visited, rdata);
 			if (AST_VECTOR_SIZE(&current->children) > 0) {
 				ast_debug(1, "List %s had no successful children.\n", resource);
 				if (AST_VECTOR_APPEND(&parent->children, current)) {
@@ -1158,7 +1184,7 @@
  * \retval 300-699 Failure to subscribe to requested resource.
  */
 static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
-		const char *resource, struct resource_tree *tree, int has_eventlist_support)
+		const char *resource, struct resource_tree *tree, int has_eventlist_support, pjsip_rx_data *rdata)
 {
 	RAII_VAR(struct resource_list *, list, NULL, ao2_cleanup);
 	struct resources visited;
@@ -1170,7 +1196,7 @@
 		if (!tree->root) {
 			return 500;
 		}
-		return handler->notifier->new_subscribe(endpoint, resource);
+		return NEW_SUBSCRIBE(handler->notifier, endpoint, resource, rdata);
 	}
 
 	ast_debug(2, "Subscription '%s->%s' is a list\n",
@@ -1187,7 +1213,7 @@
 
 	tree->notification_batch_interval = list->notification_batch_interval;
 
-	build_node_children(endpoint, handler, list, tree->root, &visited);
+	build_node_children(endpoint, handler, list, tree->root, &visited, rdata);
 	AST_VECTOR_FREE(&visited);
 
 	if (AST_VECTOR_SIZE(&tree->root->children) > 0) {
@@ -1380,6 +1406,7 @@
 		sub->handler->subscription_shutdown(sub);
 	}
 }
+
 static int subscription_unreference_dialog(void *obj)
 {
 	struct sip_subscription_tree *sub_tree = obj;
@@ -1674,7 +1701,7 @@
 
 	memset(&tree, 0, sizeof(tree));
 	resp = build_resource_tree(endpoint, handler, resource, &tree,
-		ast_sip_pubsub_has_eventlist_support(rdata));
+		ast_sip_pubsub_has_eventlist_support(rdata), rdata);
 	if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
 		pj_status_t dlg_status;
 
@@ -2454,6 +2481,16 @@
 	return require;
 }
 
+static void set_state_terminated(struct ast_sip_subscription *sub)
+{
+	int i;
+
+	sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
+	for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
+		set_state_terminated(AST_VECTOR_GET(&sub->children, i));
+	}
+}
+
 /*!
  * \brief Send a NOTIFY request to a subscriber
  *
@@ -2468,6 +2505,7 @@
 {
 	pjsip_evsub *evsub = sub_tree->evsub;
 	pjsip_tx_data *tdata;
+	struct ast_sip_subscription_handler *handler;
 
 	if (ast_shutdown_final()
 		&& sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED
@@ -2491,6 +2529,13 @@
 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
 	}
 
+	handler = subscription_get_handler_from_tdata(tdata, sub_tree->persistence->endpoint);
+	if (handler->notifier->tdata_callback) {
+		/* The module for this event wants a callback to the pjsip_tx_data,
+		 * e.g. so it can add custom headers or do something custom to the response. */
+		handler->notifier->tdata_callback(sub_tree->root, tdata);
+	}
+
 	if (sip_subscription_send_request(sub_tree, tdata)) {
 		/* do not call pjsip_tx_data_dec_ref(tdata). The pjsip_dlg_send_request deletes the message on error */
 		return -1;
@@ -2954,6 +2999,7 @@
 
 	notify_data = sub->handler->notifier->get_notify_data(sub);
 	if (!notify_data) {
+		ast_debug(3, "No notify data, terminating\n");
 		return -1;
 	}
 
@@ -3085,7 +3131,7 @@
 
 	memset(&tree, 0, sizeof(tree));
 	resp = build_resource_tree(endpoint, handler, resource, &tree,
-		ast_sip_pubsub_has_eventlist_support(rdata));
+		ast_sip_pubsub_has_eventlist_support(rdata), rdata);
 	if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
 		resource_tree_destroy(&tree);
@@ -3095,6 +3141,7 @@
 	sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
 	if (!sub_tree) {
 		if (dlg_status != PJ_EEXISTS) {
+			ast_debug(3, "No dialog exists, rejecting\n");
 			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
 		}
 	} else {
@@ -3331,6 +3378,7 @@
 	publication->handler = handler;
 	if (publication->handler->publication_state_change(publication, rdata->msg_info.msg->body,
 			AST_SIP_PUBLISH_STATE_INITIALIZED)) {
+		ast_debug(3, "Publication state change failed\n");
 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
 		ao2_cleanup(publication);
 		return NULL;
@@ -3760,16 +3808,6 @@
 	return PJ_FALSE;
 }
 
-static void set_state_terminated(struct ast_sip_subscription *sub)
-{
-	int i;
-
-	sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
-	for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
-		set_state_terminated(AST_VECTOR_GET(&sub->children, i));
-	}
-}
-
 /*!
  * \brief Callback sequence for subscription terminate:
  *
@@ -3852,7 +3890,8 @@
 
 
 /* The code in this function was previously in pubsub_on_evsub_state. */
-static void clean_sub_tree(pjsip_evsub *evsub){
+static void clean_sub_tree(pjsip_evsub *evsub)
+{
 
 	struct sip_subscription_tree *sub_tree;
 	sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
@@ -3912,7 +3951,6 @@
 		return;
 	}
 
-
 	/* It's easier to write this as what we WANT to process, then negate it. */
 	if (!(sub_tree->state == SIP_SUB_TREE_TERMINATE_IN_PROGRESS
 		|| (event->type == PJSIP_EVENT_TSX_STATE && sub_tree->state == SIP_SUB_TREE_NORMAL)
@@ -3927,9 +3965,8 @@
 	   This was previously handled by pubsub_on_rx_refresh setting:
 	   'sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING' */
 	if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
-	    !pjsip_method_cmp(&event->body.tsx_state.tsx->method, &pjsip_subscribe_method) &&
-	    pjsip_evsub_get_expires(evsub) == 0) {
-
+		!pjsip_method_cmp(&event->body.tsx_state.tsx->method, &pjsip_subscribe_method) &&
+		pjsip_evsub_get_expires(evsub) == 0) {
 		ast_debug(3, "Subscription ending, do nothing.\n");
 		return;
 	}
@@ -4058,6 +4095,8 @@
 		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
 {
 	struct sip_subscription_tree *sub_tree;
+	struct ast_sip_subscription_handler *handler = NULL;
+	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
 
 	sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
 	ast_debug(3, "evsub %p sub_tree %p sub_tree state %s\n", evsub, sub_tree,
@@ -4085,25 +4124,32 @@
 		sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
 	}
 
+	endpoint = ast_pjsip_rdata_get_endpoint(rdata);
+	handler = endpoint ? subscription_get_handler_from_rdata(rdata, ast_sorcery_object_get_id(endpoint)) : NULL;
+
+	/* If the handler wants a callback on refresh, then do it (some protocols require this). */
+	if (sub_tree->state == SIP_SUB_TREE_NORMAL && handler && handler->notifier->refresh_subscribe) {
+		if (!handler->notifier->refresh_subscribe(sub_tree->root, rdata)) {
+			return; /* If the callback handled it, we're done. */
+		}
+	}
+
 	if (sub_tree->state == SIP_SUB_TREE_NORMAL && sub_tree->is_list) {
 		/* update RLS */
 		const char *resource = sub_tree->root->resource;
 		struct ast_sip_subscription *old_root = sub_tree->root;
 		struct ast_sip_subscription *new_root = NULL;
-		RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
-		struct ast_sip_subscription_handler *handler = NULL;
+
 		struct ast_sip_pubsub_body_generator *generator = NULL;
 
-		if ((endpoint = ast_pjsip_rdata_get_endpoint(rdata))
-			&& (handler = subscription_get_handler_from_rdata(rdata, ast_sorcery_object_get_id(endpoint)))
-			&& (generator = subscription_get_generator_from_rdata(rdata, handler))) {
+		if (endpoint && handler && (generator = subscription_get_generator_from_rdata(rdata, handler))) {
 
 			struct resource_tree tree;
 			int resp;
 
 			memset(&tree, 0, sizeof(tree));
 			resp = build_resource_tree(endpoint, handler, resource, &tree,
-				ast_sip_pubsub_has_eventlist_support(rdata));
+				ast_sip_pubsub_has_eventlist_support(rdata), rdata);
 			if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
 				new_root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree.root);
 				if (new_root) {
@@ -5330,7 +5376,7 @@
 	}
 
 	tree = ast_calloc(1, sizeof(*tree));
-	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
 	if (resp != 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;
@@ -5400,7 +5446,7 @@
 	}
 
 	tree = ast_calloc(1, sizeof(*tree));
-	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
 	if (resp != 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;
@@ -5461,7 +5507,7 @@
 	}
 
 	tree = ast_calloc(1, sizeof(*tree));
-	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
 	if (resp != 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;
@@ -5530,7 +5576,7 @@
 	}
 
 	tree = ast_calloc(1, sizeof(*tree));
-	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
 	if (resp != 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;
@@ -5603,7 +5649,7 @@
 	}
 
 	tree = ast_calloc(1, sizeof(*tree));
-	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
 	if (resp != 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;
@@ -5675,7 +5721,7 @@
 	}
 
 	tree = ast_calloc(1, sizeof(*tree));
-	resp = build_resource_tree(NULL, &test_handler, "herp", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "herp", tree, 1, NULL);
 	if (resp == 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;
@@ -5722,7 +5768,7 @@
 	/* Since the test_handler is for event "test", this should not build a list, but
 	 * instead result in a single resource being created, called "foo"
 	 */
-	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
+	resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
 	if (resp != 200) {
 		ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
 		return AST_TEST_FAIL;

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/20031
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: I5c54d32223eedab7460ca28616ed50ba4ce1341f
Gerrit-Change-Number: 20031
Gerrit-PatchSet: 1
Gerrit-Owner: N A <asterisk at phreaknet.org>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20230403/2ba54465/attachment-0001.html>


More information about the asterisk-code-review mailing list