Skip to content

Commit 659f2aa

Browse files
authored
res_pjsip_pubsub: Add new pubsub module capabilities. (#82)
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. Resolves: #81 ASTERISK-30485 #close Master-Only: True
1 parent cd28651 commit 659f2aa

File tree

2 files changed

+92
-39
lines changed

2 files changed

+92
-39
lines changed

include/asterisk/res_pjsip_pubsub.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ enum ast_sip_subscription_notify_reason {
232232
#define AST_SIP_EXTEN_STATE_DATA "ast_sip_exten_state_data"
233233
/*! Type used for conveying mailbox state */
234234
#define AST_SIP_MESSAGE_ACCUMULATOR "ast_sip_message_accumulator"
235+
/*! Type used for device feature synchronization */
236+
#define AST_SIP_DEVICE_FEATURE_SYNC_DATA "ast_sip_device_feature_sync_data"
235237

236238
/*!
237239
* \brief Data used to create bodies for NOTIFY/PUBLISH requests.
@@ -268,6 +270,18 @@ struct ast_sip_notifier {
268270
* \return The response code to send to the SUBSCRIBE.
269271
*/
270272
int (*new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource);
273+
/*!
274+
* \brief Same as new_subscribe, but also pass a handle to the pjsip_rx_data
275+
*
276+
* \note If this callback exists, it will be executed, otherwise new_subscribe will be.
277+
* Only use this if you need the rdata. Otherwise, use new_subscribe.
278+
*
279+
* \param endpoint The endpoint from which we received the SUBSCRIBE
280+
* \param resource The name of the resource to which the subscription is being made
281+
* \param rdata The pjsip_rx_data for incoming subscription
282+
* \return The response code to send to the SUBSCRIBE.
283+
*/
284+
int (*new_subscribe_with_rdata)(struct ast_sip_endpoint *endpoint, const char *resource, pjsip_rx_data *rdata);
271285
/*!
272286
* \brief Called when an inbound subscription has been accepted.
273287
*
@@ -282,6 +296,25 @@ struct ast_sip_notifier {
282296
* \retval -1 Failure
283297
*/
284298
int (*subscription_established)(struct ast_sip_subscription *sub);
299+
/*!
300+
* \brief Called when a SUBSCRIBE arrives for an already active subscription.
301+
*
302+
* \param sub The existing subscription
303+
* \retval 0 Success
304+
* \retval -1 Failure
305+
*/
306+
int (*refresh_subscribe)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata);
307+
/*!
308+
* \brief Optional callback to execute before sending outgoing NOTIFY requests.
309+
* Because res_pjsip_pubsub creates the tdata internally, this allows modules
310+
* to access the tdata if needed, e.g. to add custom headers.
311+
*
312+
* \param sub The existing subscription
313+
* \param tdata The pjsip_tx_data to use for the outgoing NOTIFY
314+
* \retval 0 Success
315+
* \retval -1 Failure
316+
*/
317+
int (*notify_created)(struct ast_sip_subscription *sub, pjsip_tx_data *tdata);
285318
/*!
286319
* \brief Supply data needed to create a NOTIFY body.
287320
*

res/res_pjsip_pubsub.c

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,8 @@ static int have_visited(const char *resource, struct resources *visited)
10181018
return 0;
10191019
}
10201020

1021+
#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)
1022+
10211023
/*!
10221024
* \brief Build child nodes for a given parent.
10231025
*
@@ -1040,7 +1042,7 @@ static int have_visited(const char *resource, struct resources *visited)
10401042
* \param visited The resources that have already been visited.
10411043
*/
10421044
static void build_node_children(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
1043-
struct resource_list *list, struct tree_node *parent, struct resources *visited)
1045+
struct resource_list *list, struct tree_node *parent, struct resources *visited, pjsip_rx_data *rdata)
10441046
{
10451047
int i;
10461048

@@ -1056,7 +1058,7 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
10561058

10571059
child_list = retrieve_resource_list(resource, list->event);
10581060
if (!child_list) {
1059-
int resp = handler->notifier->new_subscribe(endpoint, resource);
1061+
int resp = NEW_SUBSCRIBE(handler->notifier, endpoint, resource, rdata);
10601062
if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
10611063
char display_name[AST_MAX_EXTENSION] = "";
10621064
if (list->resource_display_name && handler->notifier->get_resource_display_name) {
@@ -1085,7 +1087,7 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
10851087
ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
10861088
continue;
10871089
}
1088-
build_node_children(endpoint, handler, child_list, current, visited);
1090+
build_node_children(endpoint, handler, child_list, current, visited, rdata);
10891091
if (AST_VECTOR_SIZE(&current->children) > 0) {
10901092
ast_debug(1, "List %s had no successful children.\n", resource);
10911093
if (AST_VECTOR_APPEND(&parent->children, current)) {
@@ -1158,19 +1160,21 @@ static void resource_tree_destroy(struct resource_tree *tree)
11581160
* \retval 300-699 Failure to subscribe to requested resource.
11591161
*/
11601162
static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
1161-
const char *resource, struct resource_tree *tree, int has_eventlist_support)
1163+
const char *resource, struct resource_tree *tree, int has_eventlist_support, pjsip_rx_data *rdata)
11621164
{
11631165
RAII_VAR(struct resource_list *, list, NULL, ao2_cleanup);
11641166
struct resources visited;
11651167

1166-
if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
1168+
int not_eventlist_but_needs_children = !strcmp(handler->body_type, AST_SIP_DEVICE_FEATURE_SYNC_DATA);
1169+
1170+
if ((!has_eventlist_support && !not_eventlist_but_needs_children) || !(list = retrieve_resource_list(resource, handler->event_name))) {
11671171
ast_debug(2, "Subscription '%s->%s' is not to a list\n",
11681172
ast_sorcery_object_get_id(endpoint), resource);
11691173
tree->root = tree_node_alloc(resource, NULL, 0, NULL);
11701174
if (!tree->root) {
11711175
return 500;
11721176
}
1173-
return handler->notifier->new_subscribe(endpoint, resource);
1177+
return NEW_SUBSCRIBE(handler->notifier, endpoint, resource, rdata);
11741178
}
11751179

11761180
ast_debug(2, "Subscription '%s->%s' is a list\n",
@@ -1187,7 +1191,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
11871191

11881192
tree->notification_batch_interval = list->notification_batch_interval;
11891193

1190-
build_node_children(endpoint, handler, list, tree->root, &visited);
1194+
build_node_children(endpoint, handler, list, tree->root, &visited, rdata);
11911195
AST_VECTOR_FREE(&visited);
11921196

11931197
if (AST_VECTOR_SIZE(&tree->root->children) > 0) {
@@ -1380,6 +1384,7 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
13801384
sub->handler->subscription_shutdown(sub);
13811385
}
13821386
}
1387+
13831388
static int subscription_unreference_dialog(void *obj)
13841389
{
13851390
struct sip_subscription_tree *sub_tree = obj;
@@ -1674,7 +1679,7 @@ static int sub_persistence_recreate(void *obj)
16741679

16751680
memset(&tree, 0, sizeof(tree));
16761681
resp = build_resource_tree(endpoint, handler, resource, &tree,
1677-
ast_sip_pubsub_has_eventlist_support(rdata));
1682+
ast_sip_pubsub_has_eventlist_support(rdata), rdata);
16781683
if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
16791684
pj_status_t dlg_status;
16801685

@@ -2454,6 +2459,16 @@ static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
24542459
return require;
24552460
}
24562461

2462+
static void set_state_terminated(struct ast_sip_subscription *sub)
2463+
{
2464+
int i;
2465+
2466+
sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
2467+
for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
2468+
set_state_terminated(AST_VECTOR_GET(&sub->children, i));
2469+
}
2470+
}
2471+
24572472
/*!
24582473
* \brief Send a NOTIFY request to a subscriber
24592474
*
@@ -2491,6 +2506,12 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
24912506
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
24922507
}
24932508

2509+
if (sub_tree->root->handler->notifier->notify_created) {
2510+
/* The module for this event wants a callback to the pjsip_tx_data,
2511+
* e.g. so it can add custom headers or do something custom to the response. */
2512+
sub_tree->root->handler->notifier->notify_created(sub_tree->root, tdata);
2513+
}
2514+
24942515
if (sip_subscription_send_request(sub_tree, tdata)) {
24952516
/* do not call pjsip_tx_data_dec_ref(tdata). The pjsip_dlg_send_request deletes the message on error */
24962517
return -1;
@@ -2954,6 +2975,7 @@ static int generate_initial_notify(struct ast_sip_subscription *sub)
29542975

29552976
notify_data = sub->handler->notifier->get_notify_data(sub);
29562977
if (!notify_data) {
2978+
ast_debug(3, "No notify data, not generating any body content\n");
29572979
return -1;
29582980
}
29592981

@@ -3085,7 +3107,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
30853107

30863108
memset(&tree, 0, sizeof(tree));
30873109
resp = build_resource_tree(endpoint, handler, resource, &tree,
3088-
ast_sip_pubsub_has_eventlist_support(rdata));
3110+
ast_sip_pubsub_has_eventlist_support(rdata), rdata);
30893111
if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
30903112
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
30913113
resource_tree_destroy(&tree);
@@ -3095,6 +3117,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
30953117
sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
30963118
if (!sub_tree) {
30973119
if (dlg_status != PJ_EEXISTS) {
3120+
ast_debug(3, "No dialog exists, rejecting\n");
30983121
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
30993122
}
31003123
} else {
@@ -3331,6 +3354,7 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
33313354
publication->handler = handler;
33323355
if (publication->handler->publication_state_change(publication, rdata->msg_info.msg->body,
33333356
AST_SIP_PUBLISH_STATE_INITIALIZED)) {
3357+
ast_debug(3, "Publication state change failed\n");
33343358
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
33353359
ao2_cleanup(publication);
33363360
return NULL;
@@ -3760,16 +3784,6 @@ static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
37603784
return PJ_FALSE;
37613785
}
37623786

3763-
static void set_state_terminated(struct ast_sip_subscription *sub)
3764-
{
3765-
int i;
3766-
3767-
sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
3768-
for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
3769-
set_state_terminated(AST_VECTOR_GET(&sub->children, i));
3770-
}
3771-
}
3772-
37733787
/*!
37743788
* \brief Callback sequence for subscription terminate:
37753789
*
@@ -3852,7 +3866,8 @@ static void set_state_terminated(struct ast_sip_subscription *sub)
38523866

38533867

38543868
/* The code in this function was previously in pubsub_on_evsub_state. */
3855-
static void clean_sub_tree(pjsip_evsub *evsub){
3869+
static void clean_sub_tree(pjsip_evsub *evsub)
3870+
{
38563871

38573872
struct sip_subscription_tree *sub_tree;
38583873
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
@@ -3917,7 +3932,6 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
39173932
return;
39183933
}
39193934

3920-
39213935
/* It's easier to write this as what we WANT to process, then negate it. */
39223936
if (!(sub_tree->state == SIP_SUB_TREE_TERMINATE_IN_PROGRESS
39233937
|| (event->type == PJSIP_EVENT_TSX_STATE && sub_tree->state == SIP_SUB_TREE_NORMAL)
@@ -3932,9 +3946,8 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
39323946
This was previously handled by pubsub_on_rx_refresh setting:
39333947
'sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING' */
39343948
if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
3935-
!pjsip_method_cmp(&event->body.tsx_state.tsx->method, &pjsip_subscribe_method) &&
3936-
pjsip_evsub_get_expires(evsub) == 0) {
3937-
3949+
!pjsip_method_cmp(&event->body.tsx_state.tsx->method, &pjsip_subscribe_method) &&
3950+
pjsip_evsub_get_expires(evsub) == 0) {
39383951
ast_debug(3, "Subscription ending, do nothing.\n");
39393952
return;
39403953
}
@@ -4063,6 +4076,7 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
40634076
int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
40644077
{
40654078
struct sip_subscription_tree *sub_tree;
4079+
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
40664080

40674081
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
40684082
ast_debug(3, "evsub %p sub_tree %p sub_tree state %s\n", evsub, sub_tree,
@@ -4090,27 +4104,33 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
40904104
sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
40914105
}
40924106

4107+
endpoint = ast_pjsip_rdata_get_endpoint(rdata);
4108+
4109+
/* If the handler wants a callback on refresh, then do it (some protocols require this). */
4110+
if (sub_tree->state == SIP_SUB_TREE_NORMAL && sub_tree->root->handler->notifier->refresh_subscribe) {
4111+
if (!sub_tree->root->handler->notifier->refresh_subscribe(sub_tree->root, rdata)) {
4112+
return; /* If the callback handled it, we're done. */
4113+
}
4114+
}
4115+
40934116
if (sub_tree->state == SIP_SUB_TREE_NORMAL && sub_tree->is_list) {
40944117
/* update RLS */
40954118
const char *resource = sub_tree->root->resource;
40964119
struct ast_sip_subscription *old_root = sub_tree->root;
40974120
struct ast_sip_subscription *new_root = NULL;
4098-
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
4099-
struct ast_sip_subscription_handler *handler = NULL;
4121+
41004122
struct ast_sip_pubsub_body_generator *generator = NULL;
41014123

4102-
if ((endpoint = ast_pjsip_rdata_get_endpoint(rdata))
4103-
&& (handler = subscription_get_handler_from_rdata(rdata, ast_sorcery_object_get_id(endpoint)))
4104-
&& (generator = subscription_get_generator_from_rdata(rdata, handler))) {
4124+
if (endpoint && (generator = subscription_get_generator_from_rdata(rdata, sub_tree->root->handler))) {
41054125

41064126
struct resource_tree tree;
41074127
int resp;
41084128

41094129
memset(&tree, 0, sizeof(tree));
4110-
resp = build_resource_tree(endpoint, handler, resource, &tree,
4111-
ast_sip_pubsub_has_eventlist_support(rdata));
4130+
resp = build_resource_tree(endpoint, sub_tree->root->handler, resource, &tree,
4131+
ast_sip_pubsub_has_eventlist_support(rdata), rdata);
41124132
if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
4113-
new_root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree.root);
4133+
new_root = create_virtual_subscriptions(sub_tree->root->handler, resource, generator, sub_tree, tree.root);
41144134
if (new_root) {
41154135
if (cmp_subscription_childrens(old_root, new_root)) {
41164136
ast_debug(1, "RLS '%s->%s' was modified, regenerate it\n", ast_sorcery_object_get_id(endpoint), old_root->resource);
@@ -5335,7 +5355,7 @@ AST_TEST_DEFINE(resource_tree)
53355355
}
53365356

53375357
tree = ast_calloc(1, sizeof(*tree));
5338-
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5358+
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
53395359
if (resp != 200) {
53405360
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
53415361
return AST_TEST_FAIL;
@@ -5405,7 +5425,7 @@ AST_TEST_DEFINE(complex_resource_tree)
54055425
}
54065426

54075427
tree = ast_calloc(1, sizeof(*tree));
5408-
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5428+
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
54095429
if (resp != 200) {
54105430
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
54115431
return AST_TEST_FAIL;
@@ -5466,7 +5486,7 @@ AST_TEST_DEFINE(bad_resource)
54665486
}
54675487

54685488
tree = ast_calloc(1, sizeof(*tree));
5469-
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5489+
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
54705490
if (resp != 200) {
54715491
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
54725492
return AST_TEST_FAIL;
@@ -5535,7 +5555,7 @@ AST_TEST_DEFINE(bad_branch)
55355555
}
55365556

55375557
tree = ast_calloc(1, sizeof(*tree));
5538-
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5558+
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
55395559
if (resp != 200) {
55405560
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
55415561
return AST_TEST_FAIL;
@@ -5608,7 +5628,7 @@ AST_TEST_DEFINE(duplicate_resource)
56085628
}
56095629

56105630
tree = ast_calloc(1, sizeof(*tree));
5611-
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5631+
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
56125632
if (resp != 200) {
56135633
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
56145634
return AST_TEST_FAIL;
@@ -5680,7 +5700,7 @@ AST_TEST_DEFINE(loop)
56805700
}
56815701

56825702
tree = ast_calloc(1, sizeof(*tree));
5683-
resp = build_resource_tree(NULL, &test_handler, "herp", tree, 1);
5703+
resp = build_resource_tree(NULL, &test_handler, "herp", tree, 1, NULL);
56845704
if (resp == 200) {
56855705
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
56865706
return AST_TEST_FAIL;
@@ -5727,7 +5747,7 @@ AST_TEST_DEFINE(bad_event)
57275747
/* Since the test_handler is for event "test", this should not build a list, but
57285748
* instead result in a single resource being created, called "foo"
57295749
*/
5730-
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5750+
resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
57315751
if (resp != 200) {
57325752
ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
57335753
return AST_TEST_FAIL;

0 commit comments

Comments
 (0)