[Asterisk-code-review] res_pjsip_pubsub: Add ability to persist generator state information. (asterisk[16])

Friendly Automation asteriskteam at digium.com
Thu Jan 9 16:25:22 CST 2020


Friendly Automation has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/13581 )

Change subject: res_pjsip_pubsub: Add ability to persist generator state information.
......................................................................

res_pjsip_pubsub: Add ability to persist generator state information.

Some body generators, such as dialog-info+xml, require storing state
information which is then conveyed in the NOTIFY request itself. Up
until now there was no way for such body generators to persist this
information.

Two new API calls have been added to allow body generators to set and
get persisted data. This data is persisted out alongside the normal
persistence information and allows the body generator to restore
state information or to simply use this for normal storage of state.
State is stored in the form of JSON and it is up to the body
generator to interpret this as needed.

The dialog-info+xml body generator has been updated to take advantage
of this to persist the version number.

ASTERISK-27759

Change-Id: I5fda56c624fd13c17b3c48e0319b77079e9e27de
---
M include/asterisk/res_pjsip_pubsub.h
M res/res_pjsip_dialog_info_body_generator.c
M res/res_pjsip_pubsub.c
3 files changed, 143 insertions(+), 47 deletions(-)

Approvals:
  George Joseph: Looks good to me, but someone else must approve
  Kevin Harwell: Looks good to me, approved
  Friendly Automation: Approved for Submit



diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index e9d59a0..4309a09 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -546,6 +546,29 @@
 struct ao2_container *ast_sip_subscription_get_datastores(const struct ast_sip_subscription *subscription);
 
 /*!
+ * \since 13.31.0
+ * \since 16.8.0
+ * \since 17.2.0
+ * \brief Set persistence data for a subscription
+ *
+ * \param subscription The subscription to set persistence data on
+ * \param persistence_data The persistence data to set
+ *
+ * \note This steals the reference to persistence_data
+ */
+void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data);
+
+/*!
+ * \since 13.31.0
+ * \since 16.8.0
+ * \since 17.2.0
+ * \brief Retrieve persistence data for a subscription
+ *
+ * \param subscription The subscription to retrieve persistence data from
+ */
+const struct ast_json *ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription);
+
+/*!
  * \brief Register a subscription handler
  *
  * \retval 0 Handler was registered successfully
diff --git a/res/res_pjsip_dialog_info_body_generator.c b/res/res_pjsip_dialog_info_body_generator.c
index a13995f..3934a8a 100644
--- a/res/res_pjsip_dialog_info_body_generator.c
+++ b/res/res_pjsip_dialog_info_body_generator.c
@@ -60,51 +60,15 @@
 	return ast_sip_presence_xml_create_node(state_data->pool, NULL, "dialog-info");
 }
 
-static struct ast_datastore *dialog_info_xml_state_find_or_create(struct ao2_container *datastores)
-{
-	struct ast_datastore *datastore = ast_datastores_find(datastores, "dialog-info+xml");
-
-	if (datastore) {
-		return datastore;
-	}
-
-	datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");
-	if (!datastore) {
-		return NULL;
-	}
-	datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));
-	if (!datastore->data || ast_datastores_add(datastores, datastore)) {
-		ao2_ref(datastore, -1);
-		return NULL;
-	}
-
-	return datastore;
-}
-
-static unsigned int dialog_info_xml_get_version(struct ao2_container *datastores, unsigned int *version)
-{
-	struct ast_datastore *datastore = dialog_info_xml_state_find_or_create(datastores);
-	struct dialog_info_xml_state *state;
-
-	if (!datastore) {
-		return -1;
-	}
-
-	state = datastore->data;
-	*version = state->version++;
-	ao2_ref(datastore, -1);
-
-	return 0;
-}
-
 static int dialog_info_generate_body_content(void *body, void *data)
 {
 	pj_xml_node *dialog_info = body, *dialog, *state;
+	struct ast_datastore *datastore;
+	struct dialog_info_xml_state *datastore_state;
 	struct ast_sip_exten_state_data *state_data = data;
 	char *local = ast_strdupa(state_data->local), *stripped, *statestring = NULL;
 	char *pidfstate = NULL, *pidfnote = NULL;
 	enum ast_sip_pidf_state local_state;
-	unsigned int version;
 	char version_str[32], sanitized[PJSIP_MAX_URL_SIZE];
 	struct ast_sip_endpoint *endpoint = NULL;
 	unsigned int notify_early_inuse_ringing = 0;
@@ -113,9 +77,35 @@
 		return -1;
 	}
 
-	if (dialog_info_xml_get_version(state_data->datastores, &version)) {
-		ast_log(LOG_WARNING, "dialog-info+xml version could not be retrieved from datastore\n");
-		return -1;
+	datastore = ast_datastores_find(state_data->datastores, "dialog-info+xml");
+	if (!datastore) {
+		const struct ast_json *version_json = NULL;
+
+		datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");
+		if (!datastore) {
+			return -1;
+		}
+
+		datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));
+		if (!datastore->data || ast_datastores_add(state_data->datastores, datastore)) {
+			ao2_ref(datastore, -1);
+			return -1;
+		}
+		datastore_state = datastore->data;
+
+		if (state_data->sub) {
+			version_json = ast_sip_subscription_get_persistence_data(state_data->sub);
+		}
+
+		if (version_json) {
+			datastore_state->version = ast_json_integer_get(version_json);
+			datastore_state->version++;
+		} else {
+			datastore_state->version = 0;
+		}
+	} else {
+		datastore_state = datastore->data;
+		datastore_state->version++;
 	}
 
 	stripped = ast_strip_quoted(local, "<", ">");
@@ -130,9 +120,13 @@
 
 	ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "xmlns", "urn:ietf:params:xml:ns:dialog-info");
 
-	snprintf(version_str, sizeof(version_str), "%u", version);
+	snprintf(version_str, sizeof(version_str), "%u", datastore_state->version);
 	ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "version", version_str);
 
+	if (state_data->sub) {
+		ast_sip_subscription_set_persistence_data(state_data->sub, ast_json_integer_create(datastore_state->version));
+	}
+
 	ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "state", "full");
 	ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "entity", sanitized);
 
@@ -156,6 +150,8 @@
 		ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");
 	}
 
+	ao2_ref(datastore, -1);
+
 	return 0;
 }
 
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index e26f250..32e0adf 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -132,6 +132,11 @@
 					and therefore the subscription must be deleted after an asterisk restart.
 					</synopsis>
 				</configOption>
+				<configOption name="generator_data">
+					<synopsis>If set, contains persistence data for all generators of content
+					for the subscription.
+					</synopsis>
+				</configOption>
 			</configObject>
 			<configObject name="resource_list">
 				<synopsis>Resource list configuration parameters.</synopsis>
@@ -389,6 +394,8 @@
 	char contact_uri[PJSIP_MAX_URL_SIZE];
 	/*! Prune subscription on restart */
 	int prune_on_boot;
+	/*! Body generator specific persistence data */
+	struct ast_json *generator_data;
 };
 
 /*!
@@ -490,6 +497,8 @@
 	unsigned int full_state;
 	/*! URI associated with the subscription */
 	pjsip_sip_uri *uri;
+	/*! Data to be persisted with the subscription */
+	struct ast_json *persistence_data;
 	/*! Name of resource being subscribed to */
 	char resource[0];
 };
@@ -615,6 +624,7 @@
 
 	ast_free(persistence->endpoint);
 	ast_free(persistence->tag);
+	ast_json_unref(persistence->generator_data);
 }
 
 /*! \brief Allocator for subscription persistence */
@@ -1198,6 +1208,7 @@
 
 	AST_VECTOR_FREE(&sub->children);
 	ao2_cleanup(sub->datastores);
+	ast_json_unref(sub->persistence_data);
 	ast_free(sub);
 }
 
@@ -1248,6 +1259,14 @@
 	pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, contact_uri);
 	pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);
 
+	/* If there is any persistence information available for this subscription that was persisted
+	 * then make it available so that the NOTIFY has the correct state.
+	 */
+
+	if (tree->persistence && tree->persistence->generator_data) {
+		sub->persistence_data = ast_json_object_get(tree->persistence->generator_data, resource);
+	}
+
 	sub->handler = handler;
 	sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
 	sub->tree = ao2_bump(tree);
@@ -1446,11 +1465,10 @@
 static struct sip_subscription_tree *create_subscription_tree(const struct ast_sip_subscription_handler *handler,
 		struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
 		struct ast_sip_pubsub_body_generator *generator, struct resource_tree *tree,
-		pj_status_t *dlg_status)
+		pj_status_t *dlg_status, struct subscription_persistence *persistence)
 {
 	struct sip_subscription_tree *sub_tree;
 	pjsip_dialog *dlg;
-	struct subscription_persistence *persistence;
 
 	sub_tree = allocate_subscription_tree(endpoint, rdata);
 	if (!sub_tree) {
@@ -1491,6 +1509,9 @@
 
 	sub_tree->notification_batch_interval = tree->notification_batch_interval;
 
+	/* Persistence information needs to be available for all the subscriptions */
+	sub_tree->persistence = ao2_bump(persistence);
+
 	sub_tree->root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);
 	if (AST_VECTOR_SIZE(&sub_tree->root->children) > 0) {
 		sub_tree->is_list = 1;
@@ -1612,7 +1633,7 @@
 		pj_status_t dlg_status;
 
 		sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
-			&tree, &dlg_status);
+			&tree, &dlg_status, persistence);
 		if (!sub_tree) {
 			if (dlg_status != PJ_EEXISTS) {
 				ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",
@@ -1630,7 +1651,6 @@
 			ind->sub_tree = ao2_bump(sub_tree);
 			ind->expires = expires_header->ivalue;
 
-			sub_tree->persistence = ao2_bump(persistence);
 			subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_RECREATED);
 			if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) {
 				/* Could not send initial subscribe NOTIFY */
@@ -2644,6 +2664,28 @@
 	return publication->datastores;
 }
 
+void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
+{
+	ast_json_unref(subscription->persistence_data);
+	subscription->persistence_data = persistence_data;
+
+	if (subscription->tree->persistence) {
+		if (!subscription->tree->persistence->generator_data) {
+			subscription->tree->persistence->generator_data = ast_json_object_create();
+			if (!subscription->tree->persistence->generator_data) {
+				return;
+			}
+		}
+		ast_json_object_set(subscription->tree->persistence->generator_data, subscription->resource,
+			ast_json_ref(persistence_data));
+	}
+}
+
+const struct ast_json *ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)
+{
+	return subscription->persistence_data;
+}
+
 AST_RWLIST_HEAD_STATIC(publish_handlers, ast_sip_publish_handler);
 
 static int publication_hash_fn(const void *obj, const int flags)
@@ -3005,7 +3047,7 @@
 		return PJ_TRUE;
 	}
 
-	sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status);
+	sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
 	if (!sub_tree) {
 		if (dlg_status != PJ_EEXISTS) {
 			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
@@ -4657,6 +4699,39 @@
 	return 0;
 }
 
+static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct subscription_persistence *persistence = obj;
+	struct ast_json_error error;
+
+	/* We tolerate a failure of the JSON to load and instead start fresh, since this field
+	 * originates from the persistence code and not a user.
+	 */
+	persistence->generator_data = ast_json_load_string(var->value, &error);
+
+	return 0;
+}
+
+static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct subscription_persistence *persistence = obj;
+	char *value;
+
+	if (!persistence->generator_data) {
+		return 0;
+	}
+
+	value = ast_json_dump_string(persistence->generator_data);
+	if (!value) {
+		return -1;
+	}
+
+	*buf = ast_strdup(value);
+	ast_json_free(value);
+
+	return 0;
+}
+
 static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
 	struct subscription_persistence *persistence = obj;
@@ -5529,6 +5604,8 @@
 		CHARFLDSET(struct subscription_persistence, contact_uri));
 	ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "no", OPT_YESNO_T, 1,
 		FLDSET(struct subscription_persistence, prune_on_boot));
+	ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "generator_data", "",
+		persistence_generator_data_str2struct, persistence_generator_data_struct2str, NULL, 0, 0);
 
 	if (apply_list_configuration(sorcery)) {
 		ast_sched_context_destroy(sched);

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

Gerrit-Project: asterisk
Gerrit-Branch: 16
Gerrit-Change-Id: I5fda56c624fd13c17b3c48e0319b77079e9e27de
Gerrit-Change-Number: 13581
Gerrit-PatchSet: 2
Gerrit-Owner: Joshua Colp <jcolp at sangoma.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20200109/463c5c8a/attachment-0001.html>


More information about the asterisk-code-review mailing list