<p>Kevin Harwell <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13574">View Change</a></p><div style="white-space:pre-wrap">Approvals:
George Joseph: Looks good to me, but someone else must approve
Kevin Harwell: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_pubsub: Add ability to persist generator state information.<br><br>Some body generators, such as dialog-info+xml, require storing state<br>information which is then conveyed in the NOTIFY request itself. Up<br>until now there was no way for such body generators to persist this<br>information.<br><br>Two new API calls have been added to allow body generators to set and<br>get persisted data. This data is persisted out alongside the normal<br>persistence information and allows the body generator to restore<br>state information or to simply use this for normal storage of state.<br>State is stored in the form of JSON and it is up to the body<br>generator to interpret this as needed.<br><br>The dialog-info+xml body generator has been updated to take advantage<br>of this to persist the version number.<br><br>ASTERISK-27759<br><br>Change-Id: I5fda56c624fd13c17b3c48e0319b77079e9e27de<br>---<br>M include/asterisk/res_pjsip_pubsub.h<br>M res/res_pjsip_dialog_info_body_generator.c<br>M res/res_pjsip_pubsub.c<br>3 files changed, 143 insertions(+), 47 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h</span><br><span>index e9d59a0..4309a09 100644</span><br><span>--- a/include/asterisk/res_pjsip_pubsub.h</span><br><span>+++ b/include/asterisk/res_pjsip_pubsub.h</span><br><span>@@ -546,6 +546,29 @@</span><br><span> struct ao2_container *ast_sip_subscription_get_datastores(const struct ast_sip_subscription *subscription);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 13.31.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 16.8.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.2.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set persistence data for a subscription</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param subscription The subscription to set persistence data on</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param persistence_data The persistence data to set</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This steals the reference to persistence_data</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 13.31.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 16.8.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.2.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve persistence data for a subscription</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param subscription The subscription to retrieve persistence data from</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const struct ast_json *ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Register a subscription handler</span><br><span> *</span><br><span> * \retval 0 Handler was registered successfully</span><br><span>diff --git a/res/res_pjsip_dialog_info_body_generator.c b/res/res_pjsip_dialog_info_body_generator.c</span><br><span>index a13995f..3934a8a 100644</span><br><span>--- a/res/res_pjsip_dialog_info_body_generator.c</span><br><span>+++ b/res/res_pjsip_dialog_info_body_generator.c</span><br><span>@@ -60,51 +60,15 @@</span><br><span> return ast_sip_presence_xml_create_node(state_data->pool, NULL, "dialog-info");</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_datastore *dialog_info_xml_state_find_or_create(struct ao2_container *datastores)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_datastore *datastore = ast_datastores_find(datastores, "dialog-info+xml");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (datastore) {</span><br><span style="color: hsl(0, 100%, 40%);">- return datastore;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");</span><br><span style="color: hsl(0, 100%, 40%);">- if (!datastore) {</span><br><span style="color: hsl(0, 100%, 40%);">- return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));</span><br><span style="color: hsl(0, 100%, 40%);">- if (!datastore->data || ast_datastores_add(datastores, datastore)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(datastore, -1);</span><br><span style="color: hsl(0, 100%, 40%);">- return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return datastore;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static unsigned int dialog_info_xml_get_version(struct ao2_container *datastores, unsigned int *version)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_datastore *datastore = dialog_info_xml_state_find_or_create(datastores);</span><br><span style="color: hsl(0, 100%, 40%);">- struct dialog_info_xml_state *state;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (!datastore) {</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- state = datastore->data;</span><br><span style="color: hsl(0, 100%, 40%);">- *version = state->version++;</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(datastore, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static int dialog_info_generate_body_content(void *body, void *data)</span><br><span> {</span><br><span> pj_xml_node *dialog_info = body, *dialog, *state;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *datastore;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct dialog_info_xml_state *datastore_state;</span><br><span> struct ast_sip_exten_state_data *state_data = data;</span><br><span> char *local = ast_strdupa(state_data->local), *stripped, *statestring = NULL;</span><br><span> char *pidfstate = NULL, *pidfnote = NULL;</span><br><span> enum ast_sip_pidf_state local_state;</span><br><span style="color: hsl(0, 100%, 40%);">- unsigned int version;</span><br><span> char version_str[32], sanitized[PJSIP_MAX_URL_SIZE];</span><br><span> struct ast_sip_endpoint *endpoint = NULL;</span><br><span> unsigned int notify_early_inuse_ringing = 0;</span><br><span>@@ -113,9 +77,35 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (dialog_info_xml_get_version(state_data->datastores, &version)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_WARNING, "dialog-info+xml version could not be retrieved from datastore\n");</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore = ast_datastores_find(state_data->datastores, "dialog-info+xml");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_json *version_json = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore = ast_datastores_alloc_datastore(&dialog_info_xml_datastore, "dialog-info+xml");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore->data = ast_calloc(1, sizeof(struct dialog_info_xml_state));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!datastore->data || ast_datastores_add(state_data->datastores, datastore)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(datastore, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore_state = datastore->data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (state_data->sub) {</span><br><span style="color: hsl(120, 100%, 40%);">+ version_json = ast_sip_subscription_get_persistence_data(state_data->sub);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (version_json) {</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore_state->version = ast_json_integer_get(version_json);</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore_state->version++;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore_state->version = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore_state = datastore->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore_state->version++;</span><br><span> }</span><br><span> </span><br><span> stripped = ast_strip_quoted(local, "<", ">");</span><br><span>@@ -130,9 +120,13 @@</span><br><span> </span><br><span> ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "xmlns", "urn:ietf:params:xml:ns:dialog-info");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- snprintf(version_str, sizeof(version_str), "%u", version);</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(version_str, sizeof(version_str), "%u", datastore_state->version);</span><br><span> ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "version", version_str);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (state_data->sub) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_subscription_set_persistence_data(state_data->sub, ast_json_integer_create(datastore_state->version));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "state", "full");</span><br><span> ast_sip_presence_xml_create_attr(state_data->pool, dialog_info, "entity", sanitized);</span><br><span> </span><br><span>@@ -156,6 +150,8 @@</span><br><span> ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(datastore, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c</span><br><span>index e26f250..32e0adf 100644</span><br><span>--- a/res/res_pjsip_pubsub.c</span><br><span>+++ b/res/res_pjsip_pubsub.c</span><br><span>@@ -132,6 +132,11 @@</span><br><span> and therefore the subscription must be deleted after an asterisk restart.</span><br><span> </synopsis></span><br><span> </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="generator_data"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>If set, contains persistence data for all generators of content</span><br><span style="color: hsl(120, 100%, 40%);">+ for the subscription.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span> </configObject></span><br><span> <configObject name="resource_list"></span><br><span> <synopsis>Resource list configuration parameters.</synopsis></span><br><span>@@ -389,6 +394,8 @@</span><br><span> char contact_uri[PJSIP_MAX_URL_SIZE];</span><br><span> /*! Prune subscription on restart */</span><br><span> int prune_on_boot;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Body generator specific persistence data */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *generator_data;</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span>@@ -490,6 +497,8 @@</span><br><span> unsigned int full_state;</span><br><span> /*! URI associated with the subscription */</span><br><span> pjsip_sip_uri *uri;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Data to be persisted with the subscription */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *persistence_data;</span><br><span> /*! Name of resource being subscribed to */</span><br><span> char resource[0];</span><br><span> };</span><br><span>@@ -615,6 +624,7 @@</span><br><span> </span><br><span> ast_free(persistence->endpoint);</span><br><span> ast_free(persistence->tag);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_unref(persistence->generator_data);</span><br><span> }</span><br><span> </span><br><span> /*! \brief Allocator for subscription persistence */</span><br><span>@@ -1198,6 +1208,7 @@</span><br><span> </span><br><span> AST_VECTOR_FREE(&sub->children);</span><br><span> ao2_cleanup(sub->datastores);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_unref(sub->persistence_data);</span><br><span> ast_free(sub);</span><br><span> }</span><br><span> </span><br><span>@@ -1248,6 +1259,14 @@</span><br><span> pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, contact_uri);</span><br><span> pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* If there is any persistence information available for this subscription that was persisted</span><br><span style="color: hsl(120, 100%, 40%);">+ * then make it available so that the NOTIFY has the correct state.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tree->persistence && tree->persistence->generator_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sub->persistence_data = ast_json_object_get(tree->persistence->generator_data, resource);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> sub->handler = handler;</span><br><span> sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;</span><br><span> sub->tree = ao2_bump(tree);</span><br><span>@@ -1446,11 +1465,10 @@</span><br><span> static struct sip_subscription_tree *create_subscription_tree(const struct ast_sip_subscription_handler *handler,</span><br><span> struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,</span><br><span> struct ast_sip_pubsub_body_generator *generator, struct resource_tree *tree,</span><br><span style="color: hsl(0, 100%, 40%);">- pj_status_t *dlg_status)</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t *dlg_status, struct subscription_persistence *persistence)</span><br><span> {</span><br><span> struct sip_subscription_tree *sub_tree;</span><br><span> pjsip_dialog *dlg;</span><br><span style="color: hsl(0, 100%, 40%);">- struct subscription_persistence *persistence;</span><br><span> </span><br><span> sub_tree = allocate_subscription_tree(endpoint, rdata);</span><br><span> if (!sub_tree) {</span><br><span>@@ -1491,6 +1509,9 @@</span><br><span> </span><br><span> sub_tree->notification_batch_interval = tree->notification_batch_interval;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Persistence information needs to be available for all the subscriptions */</span><br><span style="color: hsl(120, 100%, 40%);">+ sub_tree->persistence = ao2_bump(persistence);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> sub_tree->root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);</span><br><span> if (AST_VECTOR_SIZE(&sub_tree->root->children) > 0) {</span><br><span> sub_tree->is_list = 1;</span><br><span>@@ -1612,7 +1633,7 @@</span><br><span> pj_status_t dlg_status;</span><br><span> </span><br><span> sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,</span><br><span style="color: hsl(0, 100%, 40%);">- &tree, &dlg_status);</span><br><span style="color: hsl(120, 100%, 40%);">+ &tree, &dlg_status, persistence);</span><br><span> if (!sub_tree) {</span><br><span> if (dlg_status != PJ_EEXISTS) {</span><br><span> ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",</span><br><span>@@ -1630,7 +1651,6 @@</span><br><span> ind->sub_tree = ao2_bump(sub_tree);</span><br><span> ind->expires = expires_header->ivalue;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- sub_tree->persistence = ao2_bump(persistence);</span><br><span> subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_RECREATED);</span><br><span> if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) {</span><br><span> /* Could not send initial subscribe NOTIFY */</span><br><span>@@ -2644,6 +2664,28 @@</span><br><span> return publication->datastores;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_unref(subscription->persistence_data);</span><br><span style="color: hsl(120, 100%, 40%);">+ subscription->persistence_data = persistence_data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subscription->tree->persistence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!subscription->tree->persistence->generator_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ subscription->tree->persistence->generator_data = ast_json_object_create();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!subscription->tree->persistence->generator_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_object_set(subscription->tree->persistence->generator_data, subscription->resource,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_ref(persistence_data));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct ast_json *ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return subscription->persistence_data;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AST_RWLIST_HEAD_STATIC(publish_handlers, ast_sip_publish_handler);</span><br><span> </span><br><span> static int publication_hash_fn(const void *obj, const int flags)</span><br><span>@@ -3005,7 +3047,7 @@</span><br><span> return PJ_TRUE;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status);</span><br><span style="color: hsl(120, 100%, 40%);">+ sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);</span><br><span> if (!sub_tree) {</span><br><span> if (dlg_status != PJ_EEXISTS) {</span><br><span> pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);</span><br><span>@@ -4657,6 +4699,39 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subscription_persistence *persistence = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json_error error;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We tolerate a failure of the JSON to load and instead start fresh, since this field</span><br><span style="color: hsl(120, 100%, 40%);">+ * originates from the persistence code and not a user.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ persistence->generator_data = ast_json_load_string(var->value, &error);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct subscription_persistence *persistence = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!persistence->generator_data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ value = ast_json_dump_string(persistence->generator_data);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf = ast_strdup(value);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_free(value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span> {</span><br><span> struct subscription_persistence *persistence = obj;</span><br><span>@@ -5529,6 +5604,8 @@</span><br><span> CHARFLDSET(struct subscription_persistence, contact_uri));</span><br><span> ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "no", OPT_YESNO_T, 1,</span><br><span> FLDSET(struct subscription_persistence, prune_on_boot));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "generator_data", "",</span><br><span style="color: hsl(120, 100%, 40%);">+ persistence_generator_data_str2struct, persistence_generator_data_struct2str, NULL, 0, 0);</span><br><span> </span><br><span> if (apply_list_configuration(sorcery)) {</span><br><span> ast_sched_context_destroy(sched);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/13574">change 13574</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/13574"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 17 </div>
<div style="display:none"> Gerrit-Change-Id: I5fda56c624fd13c17b3c48e0319b77079e9e27de </div>
<div style="display:none"> Gerrit-Change-Number: 13574 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>