Merge "res_pjsip_pubsub: Add ability to persist generator state information." into 16
This commit is contained in:
commit
3746b1e09e
|
@ -545,6 +545,29 @@ void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscrip
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
@ -60,51 +60,15 @@ static void *dialog_info_allocate_body(void *data)
|
|||
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 @@ static int dialog_info_generate_body_content(void *body, void *data)
|
|||
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 @@ static int dialog_info_generate_body_content(void *body, void *data)
|
|||
|
||||
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 @@ static int dialog_info_generate_body_content(void *body, void *data)
|
|||
ast_sip_presence_xml_create_attr(state_data->pool, param, "pvalue", "no");
|
||||
}
|
||||
|
||||
ao2_ref(datastore, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ struct subscription_persistence {
|
|||
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 @@ struct ast_sip_subscription {
|
|||
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 @@ static void subscription_persistence_destroy(void *obj)
|
|||
|
||||
ast_free(persistence->endpoint);
|
||||
ast_free(persistence->tag);
|
||||
ast_json_unref(persistence->generator_data);
|
||||
}
|
||||
|
||||
/*! \brief Allocator for subscription persistence */
|
||||
|
@ -1198,6 +1208,7 @@ static void destroy_subscription(struct ast_sip_subscription *sub)
|
|||
|
||||
AST_VECTOR_FREE(&sub->children);
|
||||
ao2_cleanup(sub->datastores);
|
||||
ast_json_unref(sub->persistence_data);
|
||||
ast_free(sub);
|
||||
}
|
||||
|
||||
|
@ -1248,6 +1259,14 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
|
|||
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 *allocate_subscription_tree(struct ast_sip_e
|
|||
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 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
|
|||
|
||||
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 @@ static int sub_persistence_recreate(void *obj)
|
|||
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 @@ static int sub_persistence_recreate(void *obj)
|
|||
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 @@ struct ao2_container *ast_sip_publication_get_datastores(const struct ast_sip_pu
|
|||
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 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
|
|||
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 @@ static int persistence_tag_struct2str(const void *obj, const intptr_t *args, cha
|
|||
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 @@ static int load_module(void)
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue