|
|
|
@ -411,6 +411,8 @@ struct sip_subscription_tree {
|
|
|
|
|
int is_list;
|
|
|
|
|
/*! Next item in the list */
|
|
|
|
|
AST_LIST_ENTRY(sip_subscription_tree) next;
|
|
|
|
|
/*! Indicates that a NOTIFY is currently being sent on the SIP subscription */
|
|
|
|
|
int last_notify;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -1063,14 +1065,28 @@ static void remove_subscription(struct sip_subscription_tree *obj)
|
|
|
|
|
AST_RWLIST_TRAVERSE_SAFE_END;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void subscription_destructor(void *obj)
|
|
|
|
|
static void destroy_subscription(struct ast_sip_subscription *sub)
|
|
|
|
|
{
|
|
|
|
|
struct ast_sip_subscription *sub = obj;
|
|
|
|
|
|
|
|
|
|
ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource);
|
|
|
|
|
ast_free(sub->body_text);
|
|
|
|
|
|
|
|
|
|
AST_VECTOR_FREE(&sub->children);
|
|
|
|
|
ao2_cleanup(sub->datastores);
|
|
|
|
|
ast_free(sub);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void destroy_subscriptions(struct ast_sip_subscription *root)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
|
|
|
|
|
struct ast_sip_subscription *child;
|
|
|
|
|
|
|
|
|
|
child = AST_VECTOR_GET(&root->children, i);
|
|
|
|
|
destroy_subscriptions(child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
destroy_subscription(root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler,
|
|
|
|
@ -1079,7 +1095,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
|
|
|
|
|
struct ast_sip_subscription *sub;
|
|
|
|
|
pjsip_sip_uri *contact_uri;
|
|
|
|
|
|
|
|
|
|
sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor);
|
|
|
|
|
sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
|
|
|
|
|
if (!sub) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -1087,13 +1103,13 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
|
|
|
|
|
|
|
|
|
|
sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
|
|
|
|
|
if (!sub->datastores) {
|
|
|
|
|
ao2_ref(sub, -1);
|
|
|
|
|
destroy_subscription(sub);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub->body_text = ast_str_create(128);
|
|
|
|
|
if (!sub->body_text) {
|
|
|
|
|
ao2_ref(sub, -1);
|
|
|
|
|
destroy_subscription(sub);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1104,7 +1120,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
|
|
|
|
|
|
|
|
|
|
sub->handler = handler;
|
|
|
|
|
sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
|
|
|
|
|
sub->tree = tree;
|
|
|
|
|
sub->tree = ao2_bump(tree);
|
|
|
|
|
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
@ -1132,6 +1148,7 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as
|
|
|
|
|
|
|
|
|
|
sub->full_state = current->full_state;
|
|
|
|
|
sub->body_generator = generator;
|
|
|
|
|
AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(¤t->children));
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < AST_VECTOR_SIZE(¤t->children); ++i) {
|
|
|
|
|
struct ast_sip_subscription *child;
|
|
|
|
@ -1166,7 +1183,6 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
|
|
|
|
|
if (AST_VECTOR_SIZE(&sub->children) > 0) {
|
|
|
|
|
for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
|
|
|
|
|
shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
|
|
|
|
|
ao2_cleanup(AST_VECTOR_GET(&sub->children, i));
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -1181,6 +1197,8 @@ static void subscription_tree_destructor(void *obj)
|
|
|
|
|
{
|
|
|
|
|
struct sip_subscription_tree *sub_tree = obj;
|
|
|
|
|
|
|
|
|
|
ast_debug(3, "Destroying subscription tree %p\n", sub_tree);
|
|
|
|
|
|
|
|
|
|
remove_subscription(sub_tree);
|
|
|
|
|
|
|
|
|
|
subscription_persistence_remove(sub_tree);
|
|
|
|
@ -1189,14 +1207,18 @@ static void subscription_tree_destructor(void *obj)
|
|
|
|
|
if (sub_tree->dlg) {
|
|
|
|
|
ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub_tree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shutdown_subscriptions(sub_tree->root);
|
|
|
|
|
ao2_cleanup(sub_tree->root);
|
|
|
|
|
destroy_subscriptions(sub_tree->root);
|
|
|
|
|
|
|
|
|
|
ast_taskprocessor_unreference(sub_tree->serializer);
|
|
|
|
|
ast_module_unref(ast_module_info->self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
|
|
|
|
|
{
|
|
|
|
|
ast_debug(3, "Removing subscription %p reference to subscription tree %p\n", sub, sub->tree);
|
|
|
|
|
ao2_cleanup(sub->tree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
|
|
|
|
|
{
|
|
|
|
|
/* We keep a reference to the dialog until our subscription is destroyed. See
|
|
|
|
@ -1654,6 +1676,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
|
|
|
|
|
#ifdef TEST_FRAMEWORK
|
|
|
|
|
struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
|
|
|
|
|
#endif
|
|
|
|
|
pjsip_evsub *evsub = sub_tree->evsub;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
if (allocate_tdata_buffer(tdata)) {
|
|
|
|
@ -1661,13 +1684,13 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res = pjsip_evsub_send_request(sub_tree->evsub, tdata) == PJ_SUCCESS ? 0 : -1;
|
|
|
|
|
res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1;
|
|
|
|
|
subscription_persistence_update(sub_tree, NULL);
|
|
|
|
|
|
|
|
|
|
ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
|
|
|
|
|
"StateText: %s\r\n"
|
|
|
|
|
"Endpoint: %s\r\n",
|
|
|
|
|
pjsip_evsub_get_state_name(sub_tree->evsub),
|
|
|
|
|
pjsip_evsub_get_state_name(evsub),
|
|
|
|
|
ast_sorcery_object_get_id(endpoint));
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
@ -2075,6 +2098,8 @@ static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Send a NOTIFY request to a subscriber
|
|
|
|
|
*
|
|
|
|
|
* \pre sub_tree->dlg is locked
|
|
|
|
|
*
|
|
|
|
|
* \param sub_tree The subscription tree representing the subscription
|
|
|
|
|
* \param force_full_state If true, ignore resource list settings and send full resource list state.
|
|
|
|
|
* \retval 0 Success
|
|
|
|
@ -2107,6 +2132,9 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
|
|
|
|
|
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
|
|
|
|
|
sub_tree->last_notify = 1;
|
|
|
|
|
}
|
|
|
|
|
if (sip_subscription_send_request(sub_tree, tdata)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
@ -2120,12 +2148,14 @@ static int serialized_send_notify(void *userdata)
|
|
|
|
|
{
|
|
|
|
|
struct sip_subscription_tree *sub_tree = userdata;
|
|
|
|
|
|
|
|
|
|
pjsip_dlg_inc_lock(sub_tree->dlg);
|
|
|
|
|
/* It's possible that between when the notification was scheduled
|
|
|
|
|
* and now, that a new SUBSCRIBE arrived, requiring full state to be
|
|
|
|
|
* sent out in an immediate NOTIFY. If that has happened, we need to
|
|
|
|
|
* bail out here instead of sending the batched NOTIFY.
|
|
|
|
|
*/
|
|
|
|
|
if (!sub_tree->send_scheduled_notify) {
|
|
|
|
|
pjsip_dlg_dec_lock(sub_tree->dlg);
|
|
|
|
|
ao2_cleanup(sub_tree);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -2135,6 +2165,7 @@ static int serialized_send_notify(void *userdata)
|
|
|
|
|
"Resource: %s",
|
|
|
|
|
sub_tree->root->resource);
|
|
|
|
|
sub_tree->notify_sched_id = -1;
|
|
|
|
|
pjsip_dlg_dec_lock(sub_tree->dlg);
|
|
|
|
|
ao2_cleanup(sub_tree);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -2167,8 +2198,18 @@ static int schedule_notification(struct sip_subscription_tree *sub_tree)
|
|
|
|
|
int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data,
|
|
|
|
|
int terminate)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
pjsip_dlg_inc_lock(sub->tree->dlg);
|
|
|
|
|
|
|
|
|
|
if (!sub->tree->evsub) {
|
|
|
|
|
pjsip_dlg_dec_lock(sub->tree->dlg);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub),
|
|
|
|
|
ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->body_text)) {
|
|
|
|
|
pjsip_dlg_dec_lock(sub->tree->dlg);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2178,9 +2219,8 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sub->tree->notification_batch_interval) {
|
|
|
|
|
return schedule_notification(sub->tree);
|
|
|
|
|
res = schedule_notification(sub->tree);
|
|
|
|
|
} else {
|
|
|
|
|
int res;
|
|
|
|
|
/* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
|
|
|
|
|
ao2_ref(sub->tree, +1);
|
|
|
|
|
res = send_notify(sub->tree, 0);
|
|
|
|
@ -2188,9 +2228,10 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
|
|
|
|
|
"Resource: %s",
|
|
|
|
|
sub->tree->root->resource);
|
|
|
|
|
ao2_ref(sub->tree, -1);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pjsip_dlg_dec_lock(sub->tree->dlg);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
|
|
|
|
@ -3139,10 +3180,87 @@ static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* XXX This function and serialized_pubsub_on_rx_refresh are nearly identical */
|
|
|
|
|
static int serialized_pubsub_on_server_timeout(void *userdata)
|
|
|
|
|
{
|
|
|
|
|
struct sip_subscription_tree *sub_tree = userdata;
|
|
|
|
|
|
|
|
|
|
pjsip_dlg_inc_lock(sub_tree->dlg);
|
|
|
|
|
if (!sub_tree->evsub) {
|
|
|
|
|
pjsip_dlg_dec_lock(sub_tree->dlg);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
set_state_terminated(sub_tree->root);
|
|
|
|
|
send_notify(sub_tree, 1);
|
|
|
|
|
ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
|
|
|
|
|
"Resource: %s",
|
|
|
|
|
sub_tree->root->resource);
|
|
|
|
|
|
|
|
|
|
pjsip_dlg_dec_lock(sub_tree->dlg);
|
|
|
|
|
ao2_cleanup(sub_tree);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief PJSIP callback when underlying SIP subscription changes state
|
|
|
|
|
*
|
|
|
|
|
* This callback is a bit of a mess, because it's not always called when
|
|
|
|
|
* you might expect it to be, and it can be called multiple times for the
|
|
|
|
|
* same state.
|
|
|
|
|
*
|
|
|
|
|
* For instance, this function is not called at all when an incoming SUBSCRIBE
|
|
|
|
|
* arrives to refresh a subscription. That makes sense in a way, since the
|
|
|
|
|
* subscription state has not made a change; it was active and remains active.
|
|
|
|
|
*
|
|
|
|
|
* However, if an incoming SUBSCRIBE arrives to end a subscription, then this
|
|
|
|
|
* will be called into once upon receiving the SUBSCRIBE (after the call to
|
|
|
|
|
* pubsub_on_rx_refresh) and again when sending a NOTIFY to end the subscription.
|
|
|
|
|
* In both cases, the apparent state of the subscription is "terminated".
|
|
|
|
|
*
|
|
|
|
|
* However, the double-terminated state changes don't happen in all cases. For
|
|
|
|
|
* instance, if a subscription expires, then the only time this callback is
|
|
|
|
|
* called is when we send the NOTIFY to end the subscription.
|
|
|
|
|
*
|
|
|
|
|
* As far as state changes are concerned, we only ever care about transitions
|
|
|
|
|
* to the "terminated" state. The action we take here is dependent on the
|
|
|
|
|
* conditions behind why the state change to "terminated" occurred. If the
|
|
|
|
|
* state change has occurred because we are sending a NOTIFY to end the
|
|
|
|
|
* subscription, we consider this to be the final hurrah of the subscription
|
|
|
|
|
* and take measures to start shutting things down. If the state change to
|
|
|
|
|
* terminated occurs for a different reason (e.g. transaction timeout,
|
|
|
|
|
* incoming SUBSCRIBE to end the subscription), then we push a task to
|
|
|
|
|
* send out a NOTIFY. When that NOTIFY is sent, this callback will be
|
|
|
|
|
* called again and we will actually shut down the subscription. The
|
|
|
|
|
* subscription tree's last_notify field let's us know if this is being
|
|
|
|
|
* called as a result of a terminating NOTIFY or not.
|
|
|
|
|
*
|
|
|
|
|
* There is no guarantee that this function will be called from a serializer
|
|
|
|
|
* thread since it can be called due to a transaction timeout. Therefore
|
|
|
|
|
* synchronization primitives are necessary to ensure that no operations
|
|
|
|
|
* step on each others' toes. The dialog lock is always held when this
|
|
|
|
|
* callback is called, so we ensure that relevant structures that may
|
|
|
|
|
* be touched in this function are always protected by the dialog lock
|
|
|
|
|
* elsewhere as well. The dialog lock in particular protects
|
|
|
|
|
*
|
|
|
|
|
* \li The subscription tree's last_notify field
|
|
|
|
|
* \li The subscription tree's evsub pointer
|
|
|
|
|
*/
|
|
|
|
|
static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
|
|
|
|
|
{
|
|
|
|
|
struct sip_subscription_tree *sub_tree;
|
|
|
|
|
|
|
|
|
|
ast_debug(3, "on_evsub_state called with state %s\n", pjsip_evsub_get_state_name(evsub));
|
|
|
|
|
|
|
|
|
|
if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -3152,21 +3270,58 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_cleanup(sub_tree);
|
|
|
|
|
if (!sub_tree->last_notify) {
|
|
|
|
|
if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, ao2_bump(sub_tree))) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to push task to send final NOTIFY.\n");
|
|
|
|
|
ao2_ref(sub_tree, -1);
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
|
|
|
|
|
sub_tree->evsub = NULL;
|
|
|
|
|
shutdown_subscriptions(sub_tree->root);
|
|
|
|
|
/* Remove evsub's reference to the sub_tree */
|
|
|
|
|
ao2_ref(sub_tree, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void set_state_terminated(struct ast_sip_subscription *sub)
|
|
|
|
|
static int serialized_pubsub_on_rx_refresh(void *userdata)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct sip_subscription_tree *sub_tree = userdata;
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
pjsip_dlg_inc_lock(sub_tree->dlg);
|
|
|
|
|
if (!sub_tree->evsub) {
|
|
|
|
|
pjsip_dlg_dec_lock(sub_tree->dlg);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
|
|
|
|
|
set_state_terminated(sub_tree->root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send_notify(sub_tree, 1);
|
|
|
|
|
|
|
|
|
|
ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
|
|
|
|
|
"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
|
|
|
|
|
"Resource: %s", sub_tree->root->resource);
|
|
|
|
|
|
|
|
|
|
pjsip_dlg_dec_lock(sub_tree->dlg);
|
|
|
|
|
ao2_cleanup(sub_tree);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Called whenever an in-dialog SUBSCRIBE is received
|
|
|
|
|
*
|
|
|
|
|
* This includes both SUBSCRIBE requests that actually refresh the subscription
|
|
|
|
|
* as well as SUBSCRIBE requests that end the subscription.
|
|
|
|
|
*
|
|
|
|
|
* In the case where the SUBSCRIBE is actually refreshing the subscription we
|
|
|
|
|
* push a task to send an appropriate NOTIFY request. In the case where the
|
|
|
|
|
* SUBSCRIBE is ending the subscription, we let the pubsub_on_evsub_state
|
|
|
|
|
* callback take care of sending the terminal NOTIFY request instead.
|
|
|
|
|
*/
|
|
|
|
|
static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
|
|
|
|
|
int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
|
|
|
|
|
{
|
|
|
|
@ -3177,31 +3332,19 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If sending a NOTIFY to terminate a subscription, then pubsub_on_evsub_state()
|
|
|
|
|
* will be called when we send the NOTIFY, and that will result in dropping the
|
|
|
|
|
* refcount of sub_tree by one, and possibly destroying the sub_tree. We need to
|
|
|
|
|
* hold a reference to the sub_tree until this function returns so that we don't
|
|
|
|
|
* try to read from or write to freed memory by accident
|
|
|
|
|
/* PJSIP will set the evsub's state to terminated before calling into this function
|
|
|
|
|
* if the Expires value of the incoming SUBSCRIBE is 0.
|
|
|
|
|
*/
|
|
|
|
|
ao2_ref(sub_tree, +1);
|
|
|
|
|
|
|
|
|
|
if (pjsip_evsub_get_state(evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
|
|
|
|
|
set_state_terminated(sub_tree->root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (send_notify(sub_tree, 1)) {
|
|
|
|
|
*p_st_code = 500;
|
|
|
|
|
if (pjsip_evsub_get_state(sub_tree->evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
|
|
|
|
|
if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_rx_refresh, ao2_bump(sub_tree))) {
|
|
|
|
|
/* If we can't push the NOTIFY refreshing task...we'll just go with it. */
|
|
|
|
|
ao2_ref(sub_tree, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
|
|
|
|
|
"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
|
|
|
|
|
"Resource: %s", sub_tree->root->resource);
|
|
|
|
|
|
|
|
|
|
if (sub_tree->is_list) {
|
|
|
|
|
pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_ref(sub_tree, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
|
|
|
|
@ -3239,31 +3382,24 @@ static void pubsub_on_client_refresh(pjsip_evsub *evsub)
|
|
|
|
|
ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int serialized_pubsub_on_server_timeout(void *userdata)
|
|
|
|
|
{
|
|
|
|
|
struct sip_subscription_tree *sub_tree = userdata;
|
|
|
|
|
|
|
|
|
|
set_state_terminated(sub_tree->root);
|
|
|
|
|
send_notify(sub_tree, 1);
|
|
|
|
|
ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
|
|
|
|
|
"Resource: %s",
|
|
|
|
|
sub_tree->root->resource);
|
|
|
|
|
|
|
|
|
|
ao2_cleanup(sub_tree);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pubsub_on_server_timeout(pjsip_evsub *evsub)
|
|
|
|
|
{
|
|
|
|
|
struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
|
|
|
|
|
|
|
|
|
|
struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
|
|
|
|
|
if (!sub_tree) {
|
|
|
|
|
/* if a subscription has been terminated and the subscription
|
|
|
|
|
timeout/expires is less than the time it takes for all pending
|
|
|
|
|
transactions to end then the subscription timer will not have
|
|
|
|
|
been canceled yet and sub will be null, so do nothing since
|
|
|
|
|
the subscription has already been terminated. */
|
|
|
|
|
return;
|
|
|
|
|
/* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
|
|
|
|
|
* with Expires: 0 arrives to end a subscription, nor does it terminate
|
|
|
|
|
* this timer when we send a NOTIFY request in response to receiving such
|
|
|
|
|
* a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
|
|
|
|
|
* NOTIFY transaction has finished (either through receiving a response
|
|
|
|
|
* or through a transaction timeout).
|
|
|
|
|
*
|
|
|
|
|
* Therefore, it is possible that we can be told that a server timeout
|
|
|
|
|
* occurred after we already thought that the subscription had been
|
|
|
|
|
* terminated. In such a case, we will have already removed the sub_tree
|
|
|
|
|
* from the evsub's mod_data array.
|
|
|
|
|
*/
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_ref(sub_tree, +1);
|
|
|
|
|