31 #include <pjsip_simple.h>
35 #include "asterisk/res_pjsip_pubsub.h"
43 #include "asterisk/res_pjsip.h"
48 #include "res_pjsip/include/res_pjsip_private.h"
49 #include "asterisk/res_pjsip_presence_xml.h"
236 static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata);
238 static struct pjsip_module pubsub_module = {
239 .name = {
"PubSub Module", 13 },
240 .priority = PJSIP_MOD_PRIORITY_APPLICATION,
241 .on_rx_request = pubsub_on_rx_request,
244 #define MOD_DATA_PERSISTENCE "sub_persistence"
245 #define MOD_DATA_MSG "sub_msg"
247 static const pj_str_t str_event_name = {
"Event", 5 };
253 #define PUBLICATIONS_BUCKETS 37
256 #define DEFAULT_PUBLISH_EXPIRES 3600
259 #define DATASTORE_BUCKETS 53
262 #define DEFAULT_EXPIRES 3600
265 const pjsip_method pjsip_publish_method =
274 enum sip_publish_type {
335 SORCERY_OBJECT(details);
351 static int esc_etag_counter;
415 enum sip_subscription_tree_state {
417 SIP_SUB_TREE_NORMAL = 0,
419 SIP_SUB_TREE_TERMINATE_PENDING,
421 SIP_SUB_TREE_TERMINATE_IN_PROGRESS,
423 SIP_SUB_TREE_TERMINATED,
426 static char *sub_tree_state_description[] = {
429 "TerminateInProgress",
448 enum ast_sip_subscription_role
role;
468 enum sip_subscription_tree_state
state;
502 AST_VECTOR(,
struct ast_sip_subscription *) children;
508 pjsip_evsub_state subscription_state;
510 unsigned int version;
512 unsigned int full_state;
535 static const char *sip_subscription_roles_map[] = {
536 [AST_SIP_SUBSCRIBER] =
"Subscriber",
537 [AST_SIP_NOTIFIER] =
"Notifier"
540 enum sip_persistence_update_type {
542 SUBSCRIPTION_PERSISTENCE_SEND_REQUEST = 0,
544 SUBSCRIPTION_PERSISTENCE_CREATED,
546 SUBSCRIPTION_PERSISTENCE_RECREATED,
548 SUBSCRIPTION_PERSISTENCE_REFRESHED,
556 static pjsip_media_type rlmi_media_type;
558 static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *
event);
559 static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
560 int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
561 static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata,
int *p_st_code,
562 pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
563 static void pubsub_on_client_refresh(pjsip_evsub *sub);
564 static void pubsub_on_server_timeout(pjsip_evsub *sub);
566 static pjsip_evsub_user pubsub_cb = {
567 .on_evsub_state = pubsub_on_evsub_state,
568 .on_rx_refresh = pubsub_on_rx_refresh,
569 .on_rx_notify = pubsub_on_rx_notify,
570 .on_client_refresh = pubsub_on_client_refresh,
571 .on_server_timeout = pubsub_on_server_timeout,
575 static void publication_resource_destroy(
void *obj)
584 static void *publication_resource_alloc(
const char *name)
589 static int sub_tree_subscription_terminate_cb(
void *data)
593 if (!sub_tree->
evsub) {
599 ast_debug(3,
"Transport destroyed. Removing subscription '%s->%s' prune on boot: %d\n",
603 sub_tree->
state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
604 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
618 static void sub_tree_transport_cb(
void *data)
636 static void subscription_persistence_destroy(
void *obj)
641 ast_free(persistence->
tag);
646 static void *subscription_persistence_alloc(
const char *name)
654 char tag[PJ_GUID_STRING_LENGTH + 1];
660 "subscription_persistence", NULL);
662 pjsip_dialog *dlg = sub_tree->
dlg;
669 ast_copy_pj_str(tag, &dlg->local.info->tag,
sizeof(tag));
678 pjsip_rx_data *rdata,
enum sip_persistence_update_type type)
686 ast_debug(3,
"Updating persistence for '%s->%s' prune on boot: %s\n",
695 pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
696 pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
698 expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
703 if (type == SUBSCRIPTION_PERSISTENCE_CREATED) {
705 !ast_sip_will_uri_survive_restart(
706 (pjsip_sip_uri *)pjsip_uri_get_uri(contact_hdr->uri),
710 ast_debug(3,
"adding transport monitor on %s for '%s->%s' prune on boot: %d\n",
711 rdata->tp_info.transport->obj_name,
714 AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(rdata->tp_info.transport,
717 sub_tree_transport_cb, sub_tree);
725 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
728 ast_log(LOG_WARNING,
"Contact not updated due to missing contact header\n");
737 if (type == SUBSCRIPTION_PERSISTENCE_CREATED
738 || type == SUBSCRIPTION_PERSISTENCE_RECREATED) {
739 if (rdata->msg_info.msg_buf) {
768 ast_debug(3,
"Unregistering transport monitor on %s '%s->%s'\n",
773 sub_tree_transport_cb, sub_tree, NULL);
784 size_t num_accept,
const char *
body_type);
789 pjsip_event_hdr *event_header;
793 event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
795 ast_log(LOG_WARNING,
"Incoming SUBSCRIBE request from %s with no Event header\n",
796 endpoint ? endpoint :
"Unknown");
799 ast_copy_pj_str(
event, &event_header->event_type,
sizeof(
event));
801 handler = find_sub_handler_for_event_name(
event);
803 ast_log(LOG_WARNING,
"No registered subscribe handler for event %s from %s\n",
event,
804 endpoint ? endpoint :
"Unknown");
822 const char *accept_exceptions[] = {
824 "application/rlmi+xml",
833 static int exceptional_accept(
const pj_str_t *
accept)
837 for (i = 0; i < ARRAY_LEN(accept_exceptions); ++i) {
838 if (!pj_strcmp2(accept, accept_exceptions[i])) {
850 pjsip_accept_hdr *accept_header = (pjsip_accept_hdr *) &rdata->msg_info.msg->hdr;
851 char accept[AST_SIP_MAX_ACCEPT][64];
852 size_t num_accept_headers = 0;
854 while ((accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, accept_header->next)) &&
855 (num_accept_headers < AST_SIP_MAX_ACCEPT)) {
858 for (i = 0; i < accept_header->count && num_accept_headers < AST_SIP_MAX_ACCEPT; ++i) {
859 if (!exceptional_accept(&accept_header->values[i])) {
860 ast_copy_pj_str(accept[num_accept_headers], &accept_header->values[i],
sizeof(accept[num_accept_headers]));
861 ++num_accept_headers;
866 if (num_accept_headers == 0) {
871 num_accept_headers = 1;
874 return find_body_generator(accept, num_accept_headers, handler->
body_type);
882 static int ast_sip_pubsub_has_eventlist_support(pjsip_rx_data *rdata)
884 pjsip_supported_hdr *supported_header = (pjsip_supported_hdr *) &rdata->msg_info.msg->hdr;
886 while ((supported_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_SUPPORTED, supported_header->next))) {
889 for (i = 0; i < supported_header->count; i++) {
890 if (!pj_stricmp2(&supported_header->values[i],
"eventlist")) {
906 unsigned int full_state;
919 static struct resource_list *retrieve_resource_list(
const char *resource,
const char *
event)
928 if (strcmp(list->
event, event)) {
929 ast_log(LOG_WARNING,
"Found resource list %s, but its event type (%s) does not match SUBSCRIBE's (%s)\n",
930 resource, list->
event, event);
953 static struct tree_node *tree_node_alloc(
const char *resource,
struct resources *visited,
unsigned int full_state,
const char *display_name)
957 node =
ast_calloc(1,
sizeof(*node) + strlen(resource) + 1);
962 strcpy(node->resource, resource);
967 node->full_state = full_state;
968 node->display_name =
ast_strdup(display_name);
985 static void tree_node_destroy(
struct tree_node *node)
996 ast_free(node->display_name);
1008 static int have_visited(
const char *resource,
struct resources *visited)
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)
1054 if (have_visited(resource, visited)) {
1055 ast_debug(1,
"Already visited resource %s. Avoiding duplicate resource or potential loop.\n", resource);
1059 child_list = retrieve_resource_list(resource, list->
event);
1061 int resp = NEW_SUBSCRIBE(handler->
notifier, endpoint, resource, rdata);
1062 if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1067 current = tree_node_alloc(resource, visited, 0, ast_strlen_zero(display_name) ? NULL : display_name);
1070 "Subscription to leaf resource %s was successful, but encountered allocation error afterwards\n",
1074 ast_debug(2,
"Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
1075 resource, parent->resource);
1077 tree_node_destroy(current);
1080 ast_debug(2,
"Subscription to leaf resource %s resulted in error response %d\n",
1084 ast_debug(2,
"Resource %s (child of %s) is a list\n", resource, parent->resource);
1085 current = tree_node_alloc(resource, visited, child_list->
full_state, NULL);
1087 ast_debug(1,
"Cannot build children of resource %s due to allocation failure\n", resource);
1090 build_node_children(endpoint, handler, child_list, current, visited, rdata);
1092 ast_debug(1,
"List %s had no successful children.\n", resource);
1094 tree_node_destroy(current);
1097 ast_debug(2,
"List %s had successful children. Adding to parent %s\n",
1098 resource, parent->resource);
1099 tree_node_destroy(current);
1101 ao2_cleanup(child_list);
1136 static void resource_tree_destroy(
struct resource_tree *tree)
1139 tree_node_destroy(tree->root);
1163 const char *resource,
struct resource_tree *tree,
int has_eventlist_support, pjsip_rx_data *rdata)
1168 int not_eventlist_but_needs_children = !strcmp(handler->
body_type, AST_SIP_DEVICE_FEATURE_SYNC_DATA);
1170 if ((!has_eventlist_support && !not_eventlist_but_needs_children) || !(list = retrieve_resource_list(resource, handler->
event_name))) {
1171 ast_debug(2,
"Subscription '%s->%s' is not to a list\n",
1173 tree->root = tree_node_alloc(resource, NULL, 0, NULL);
1177 return NEW_SUBSCRIBE(handler->
notifier, endpoint, resource, rdata);
1180 ast_debug(2,
"Subscription '%s->%s' is a list\n",
1186 tree->root = tree_node_alloc(resource, &visited, list->
full_state, NULL);
1194 build_node_children(endpoint, handler, list, tree->root, &visited, rdata);
1218 AST_RWLIST_REMOVE_CURRENT(next);
1220 ast_debug(2,
"Removing subscription '%s->%s' from list of subscriptions\n",
1226 AST_RWLIST_TRAVERSE_SAFE_END;
1230 static void destroy_subscription(
struct ast_sip_subscription *sub)
1232 ast_debug(3,
"Destroying SIP subscription from '%s->%s'\n",
1254 struct ast_sip_subscription *child;
1260 destroy_subscription(root);
1266 struct ast_sip_subscription *sub;
1268 pjsip_sip_uri *request_uri;
1270 msg = ast_sip_mod_data_get(tree->
dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
1272 ast_log(LOG_ERROR,
"No dialog message saved for SIP subscription. Cannot allocate subscription for resource %s\n", resource);
1276 sub =
ast_calloc(1,
sizeof(*sub) + strlen(resource) + 1);
1286 destroy_subscription(sub);
1292 destroy_subscription(sub);
1296 sub->
uri = pjsip_sip_uri_create(tree->
dlg->pool, PJ_FALSE);
1297 request_uri = pjsip_uri_get_uri(msg->line.req.uri);
1298 pjsip_sip_uri_assign(tree->
dlg->pool, sub->
uri, request_uri);
1299 pj_strdup2(tree->
dlg->pool, &sub->
uri->user, resource);
1330 struct ast_sip_subscription *sub;
1332 sub = allocate_subscription(handler, resource, current->display_name, tree);
1342 struct ast_sip_subscription *child;
1345 child = create_virtual_subscriptions(handler, child_node->resource, generator,
1349 ast_debug(1,
"Child subscription to resource %s could not be created\n",
1350 child_node->resource);
1355 ast_debug(1,
"Child subscription to resource %s could not be appended\n",
1356 child_node->resource);
1357 destroy_subscription(child);
1367 static void shutdown_subscriptions(
struct ast_sip_subscription *sub)
1388 static int subscription_unreference_dialog(
void *obj)
1401 pjsip_dlg_dec_session(sub_tree->
dlg, &pubsub_module);
1402 sub_tree->
dlg = NULL;
1407 static void subscription_tree_destructor(
void *obj)
1411 ast_debug(3,
"Destroying subscription tree %p '%s->%s'\n",
1418 if (sub_tree->
dlg) {
1420 subscription_unreference_dialog, sub_tree);
1429 void ast_sip_subscription_destroy(
struct ast_sip_subscription *sub)
1431 ast_debug(3,
"Removing subscription %p '%s->%s' reference to subscription tree %p\n",
1433 ao2_cleanup(sub->
tree);
1441 pjsip_evsub_set_mod_data(sub_tree->
evsub, pubsub_module.id, sub_tree);
1442 pjsip_dlg_inc_session(dlg, &pubsub_module);
1449 sub_tree = ao2_alloc(
sizeof *sub_tree, subscription_tree_destructor);
1507 struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
const char *resource,
1514 sub_tree = allocate_subscription_tree(endpoint, rdata);
1516 *dlg_status = PJ_ENOMEM;
1519 sub_tree->
role = AST_SIP_NOTIFIER;
1521 dlg = ast_sip_create_dialog_uas_locked(endpoint, rdata, dlg_status);
1523 if (*dlg_status != PJ_EEXISTS) {
1524 ast_log(LOG_WARNING,
"Unable to create dialog for SIP subscription\n");
1530 persistence = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
1531 pubsub_module.id, MOD_DATA_PERSISTENCE);
1534 pjsip_ua_unregister_dlg(pjsip_ua_instance(), dlg);
1535 pj_strdup2(dlg->pool, &dlg->local.info->tag, persistence->
tag);
1536 dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag);
1537 pjsip_ua_register_dlg(pjsip_ua_instance(), dlg);
1538 dlg->local.cseq = persistence->
cseq;
1541 pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->
evsub);
1543 subscription_setup_dialog(sub_tree, dlg);
1550 pjsip_dlg_dec_lock(dlg);
1552 #ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
1553 pjsip_evsub_add_ref(sub_tree->
evsub);
1556 ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG,
1557 pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
1564 sub_tree->
root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);
1569 add_subscription(sub_tree);
1577 unsigned int expires;
1580 static int initial_notify_task(
void *obj);
1586 pjsip_rx_data *rdata;
1597 static int sub_persistence_recreate(
void *obj)
1601 pjsip_rx_data *rdata = recreate_data->rdata;
1607 size_t resource_size;
1610 pjsip_expires_hdr *expires_header;
1612 const pj_str_t *
user;
1614 user = ast_sip_pjsip_uri_get_username(rdata->msg_info.msg->line.req.uri);
1615 resource_size = pj_strlen(user) + 1;
1617 ast_copy_pj_str(resource, user, resource_size);
1623 AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(resource);
1625 handler = subscription_get_handler_from_rdata(rdata, persistence->
endpoint);
1626 if (!handler || !handler->
notifier) {
1627 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: Could not get subscription handler.\n",
1633 generator = subscription_get_generator_from_rdata(rdata, handler);
1635 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: Body generator not available.\n",
1641 ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
1642 pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
1648 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: The endpoint was not found\n",
1655 expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
1656 rdata->msg_info.msg->hdr.next);
1657 if (!expires_header) {
1658 expires_header = pjsip_expires_hdr_create(rdata->tp_info.pool, 0);
1659 if (!expires_header) {
1660 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: Could not update expires header.\n",
1666 pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr *) expires_header);
1672 ast_debug(3,
"Expired subscription retrived from persistent store '%s' %s\n",
1678 expires_header->ivalue = expires;
1680 memset(&tree, 0,
sizeof(tree));
1681 resp = build_resource_tree(endpoint, handler, resource, &tree,
1682 ast_sip_pubsub_has_eventlist_support(rdata), rdata);
1683 if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1684 pj_status_t dlg_status;
1686 sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
1687 &tree, &dlg_status, persistence);
1689 if (dlg_status != PJ_EEXISTS) {
1690 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: Could not create subscription tree.\n",
1698 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
1702 ind->sub_tree =
ao2_bump(sub_tree);
1703 ind->expires = expires_header->ivalue;
1705 subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_RECREATED);
1708 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
1718 resource_tree_destroy(&tree);
1725 static int subscription_persistence_recreate(
void *obj,
void *arg,
int flags)
1728 pj_pool_t *
pool = arg;
1730 pjsip_rx_data rdata;
1735 ast_debug(3,
"Deleting subscription marked as 'prune' from persistent store '%s' %s\n",
1743 ast_debug(3,
"Expired subscription retrived from persistent store '%s' %s\n",
1749 memset(&rdata, 0,
sizeof(rdata));
1750 pj_pool_reset(pool);
1751 rdata.tp_info.pool =
pool;
1753 if (ast_sip_create_rdata_with_contact(&rdata, persistence->
packet, persistence->
src_name,
1756 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: The message could not be parsed\n",
1762 if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
1763 ast_log(LOG_NOTICE,
"Failed recreating '%s' subscription: Stored a SIP response instead of a request.\n",
1772 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: Could not get distributor serializer.\n",
1778 recreate_data.rdata = &rdata;
1781 ast_log(LOG_WARNING,
"Failed recreating '%s' subscription: Could not continue under distributor serializer.\n",
1791 static int subscription_persistence_load(
void *data)
1797 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
"rtd%p", PJSIP_POOL_RDATA_LEN,
1798 PJSIP_POOL_RDATA_INC);
1800 ast_log(LOG_WARNING,
"Could not create a memory pool for recreating SIP subscriptions\n");
1806 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1808 ao2_ref(persisted_subscriptions, -1);
1828 if (strcmp(type,
"FullyBooted")) {
1841 static int for_each_subscription(on_subscription_t on_subscription,
void *arg)
1846 if (!on_subscription) {
1852 if (on_subscription(i, arg)) {
1868 sip_subscription_roles_map[sub_tree->
role]);
1872 if (sub_tree->
dlg) {
1873 ast_copy_pj_str(str, &sub_tree->
dlg->call_id->id,
sizeof(str));
1881 ast_callerid_merge(str,
sizeof(str),
1895 void *ast_sip_subscription_get_header(
const struct ast_sip_subscription *sub,
const char *
header)
1902 msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
1903 pj_cstr(&name, header);
1905 return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
1912 struct ast_sip_subscription *sub;
1916 pjsip_tx_data *tdata;
1920 sub_tree = allocate_subscription_tree(endpoint, NULL);
1925 sub = allocate_subscription(handler, resource, NULL, sub_tree);
1927 ao2_cleanup(sub_tree);
1931 contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->
aors);
1932 if (!contact || ast_strlen_zero(contact->
uri)) {
1933 ast_log(LOG_WARNING,
"No contacts configured for endpoint %s. Unable to create SIP subsription\n",
1936 ao2_cleanup(contact);
1940 dlg = ast_sip_create_dialog_uac(endpoint, contact->
uri, NULL);
1941 ao2_cleanup(contact);
1943 ast_log(LOG_WARNING,
"Unable to create dialog for SIP subscription\n");
1949 pjsip_evsub_create_uac(dlg, &pubsub_cb, &event, 0, &sub_tree->
evsub);
1950 subscription_setup_dialog(sub_tree, dlg);
1952 evsub = sub_tree->
evsub;
1954 if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
1955 pjsip_evsub_send_request(sub_tree->
evsub, tdata);
1961 pjsip_evsub_terminate(evsub, PJ_TRUE);
1966 add_subscription(sub_tree);
1971 pjsip_dialog *ast_sip_subscription_get_dialog(
struct ast_sip_subscription *sub)
1973 ast_assert(sub->
tree->
dlg != NULL);
1977 struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(
struct ast_sip_subscription *sub)
1983 struct ast_taskprocessor *ast_sip_subscription_get_serializer(
struct ast_sip_subscription *sub)
2008 static int allocate_tdata_buffer(pjsip_tx_data *tdata)
2014 for (buf_size = PJSIP_MAX_PKT_LEN; size == -1 && buf_size < (PJSIP_MAX_PKT_LEN * 2); buf_size *= 2) {
2015 buf = pj_pool_alloc(tdata->pool, buf_size);
2016 size = pjsip_msg_print(tdata->msg, buf, buf_size);
2023 tdata->buf.start = buf;
2024 tdata->buf.cur = tdata->buf.start;
2025 tdata->buf.end = tdata->buf.start + buf_size;
2030 static int sip_subscription_send_request(
struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
2032 #ifdef TEST_FRAMEWORK
2034 pjsip_evsub *evsub = sub_tree->
evsub;
2038 if (allocate_tdata_buffer(tdata)) {
2039 ast_log(LOG_ERROR,
"SIP request %s is too large to send.\n", tdata->info);
2040 pjsip_tx_data_dec_ref(tdata);
2044 res = pjsip_evsub_send_request(sub_tree->
evsub, tdata);
2046 subscription_persistence_update(sub_tree, NULL, SUBSCRIPTION_PERSISTENCE_SEND_REQUEST);
2051 pjsip_evsub_get_state_name(evsub),
2054 return (res == PJ_SUCCESS ? 0 : -1);
2071 static void add_rlmi_resource(pj_pool_t *pool, pj_xml_node *rlmi,
const pjsip_generic_string_hdr *cid,
2072 const char *resource_name,
const pjsip_sip_uri *resource_uri, pjsip_evsub_state state)
2074 static pj_str_t cid_name = {
"cid", 3 };
2075 pj_xml_node *resource;
2077 pj_xml_node *instance;
2078 pj_xml_attr *cid_attr;
2080 char uri[PJSIP_MAX_URL_SIZE];
2081 char name_sanitized[PJSIP_MAX_URL_SIZE];
2084 const pj_str_t cid_stripped = {
2085 .ptr = cid->hvalue.ptr + 1,
2086 .slen = cid->hvalue.slen - 2,
2089 resource = ast_sip_presence_xml_create_node(pool, rlmi,
"resource");
2090 name = ast_sip_presence_xml_create_node(pool, resource,
"name");
2091 instance = ast_sip_presence_xml_create_node(pool, resource,
"instance");
2093 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, resource_uri, uri,
sizeof(uri));
2094 ast_sip_presence_xml_create_attr(pool, resource,
"uri", uri);
2096 ast_sip_sanitize_xml(resource_name, name_sanitized,
sizeof(name_sanitized));
2097 pj_strdup2(pool, &name->content, name_sanitized);
2101 ast_sip_presence_xml_create_attr(pool, instance,
"id",
id);
2102 ast_sip_presence_xml_create_attr(pool, instance,
"state",
2103 state == PJSIP_EVSUB_STATE_TERMINATED ?
"terminated" :
"active");
2109 cid_attr = pj_xml_attr_new(pool, &cid_name, &cid_stripped);
2110 pj_xml_add_attr(instance, cid_attr);
2126 pjsip_generic_string_hdr *
cid;
2153 static pjsip_generic_string_hdr *generate_content_id_hdr(pj_pool_t *pool,
2154 const struct ast_sip_subscription *sub)
2156 static const pj_str_t cid_name = {
"Content-ID", 10 };
2157 pjsip_generic_string_hdr *cid;
2163 alloc_size =
sizeof(id) + pj_strlen(&sub->
uri->host) + 3;
2164 cid_value.ptr = pj_pool_alloc(pool, alloc_size);
2165 cid_value.slen = sprintf(cid_value.ptr,
"<%s@%.*s>",
2167 (
int) pj_strlen(&sub->
uri->host), pj_strbuf(&sub->
uri->host));
2168 cid = pjsip_generic_string_hdr_create(pool, &cid_name, &cid_value);
2173 static int rlmi_print_body(
struct pjsip_msg_body *msg_body,
char *buf, pj_size_t size)
2176 pj_xml_node *rlmi = msg_body->data;
2178 num_printed = pj_xml_print(rlmi, buf, size, PJ_TRUE);
2179 if (num_printed <= AST_PJSIP_XML_PROLOG_LEN) {
2186 static void *rlmi_clone_data(pj_pool_t *pool,
const void *data,
unsigned len)
2188 const pj_xml_node *rlmi = data;
2190 return pj_xml_clone(pool, rlmi);
2208 static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool,
struct ast_sip_subscription *sub,
2213 pjsip_multipart_part *rlmi_part;
2214 char version_str[32];
2215 char uri[PJSIP_MAX_URL_SIZE];
2216 pjsip_generic_string_hdr *cid;
2219 rlmi = ast_sip_presence_xml_create_node(pool, NULL,
"list");
2220 ast_sip_presence_xml_create_attr(pool, rlmi,
"xmlns",
"urn:ietf:params:xml:ns:rlmi");
2222 ast_sip_subscription_get_local_uri(sub, uri,
sizeof(uri));
2223 ast_sip_presence_xml_create_attr(pool, rlmi,
"uri", uri);
2225 snprintf(version_str,
sizeof(version_str),
"%u", sub->
version++);
2226 ast_sip_presence_xml_create_attr(pool, rlmi,
"version", version_str);
2227 ast_sip_presence_xml_create_attr(pool, rlmi,
"fullState", full_state ?
"true" :
"false");
2229 name = ast_sip_presence_xml_create_node(pool, rlmi,
"name");
2230 pj_strdup2(pool, &name->content, ast_sip_subscription_get_resource_name(sub));
2238 rlmi_part = pjsip_multipart_create_part(pool);
2240 rlmi_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
2241 pjsip_media_type_cp(pool, &rlmi_part->body->content_type, &rlmi_media_type);
2243 rlmi_part->body->data = pj_xml_clone(pool, rlmi);
2244 rlmi_part->body->clone_data = rlmi_clone_data;
2245 rlmi_part->body->print_body = rlmi_print_body;
2247 cid = generate_content_id_hdr(pool, sub);
2248 pj_list_insert_before(&rlmi_part->hdr, cid);
2253 static pjsip_msg_body *generate_notify_body(pj_pool_t *pool,
struct ast_sip_subscription *root,
2254 unsigned int force_full_state);
2279 static struct body_part *allocate_body_part(pj_pool_t *pool,
const struct ast_sip_subscription *sub)
2288 bp->
cid = generate_content_id_hdr(pool, sub);
2305 static void build_body_part(pj_pool_t *pool,
struct ast_sip_subscription *sub,
2309 pjsip_msg_body *body;
2311 bp = allocate_body_part(pool, sub);
2316 body = generate_notify_body(pool, sub, use_full_state);
2323 bp->
part = pjsip_multipart_create_part(pool);
2324 bp->
part->body = body;
2325 pj_list_insert_before(&bp->
part->hdr, bp->
cid);
2338 static pjsip_msg_body *create_multipart_body(pj_pool_t *pool)
2340 pjsip_media_type media_type;
2341 pjsip_param *media_type_param;
2343 pj_str_t pj_boundary;
2345 pjsip_media_type_init2(&media_type,
"multipart",
"related");
2347 media_type_param = pj_pool_alloc(pool,
sizeof(*media_type_param));
2348 pj_list_init(media_type_param);
2350 pj_strdup2(pool, &media_type_param->name,
"type");
2351 pj_strdup2(pool, &media_type_param->value,
"\"application/rlmi+xml\"");
2353 pj_list_insert_before(&media_type.param, media_type_param);
2356 return pjsip_multipart_create(pool, &media_type, &pj_boundary);
2371 static pjsip_msg_body *generate_list_body(pj_pool_t *pool,
struct ast_sip_subscription *sub,
2372 unsigned int force_full_state)
2375 pjsip_multipart_part *rlmi_part;
2376 pjsip_msg_body *multipart;
2378 unsigned int use_full_state = force_full_state ? 1 : sub->
full_state;
2390 free_body_parts(&body_parts);
2394 multipart = create_multipart_body(pool);
2396 rlmi_part = build_rlmi_body(pool, sub, &body_parts, use_full_state);
2398 free_body_parts(&body_parts);
2401 pjsip_multipart_add_part(pool, multipart, rlmi_part);
2404 pjsip_multipart_add_part(pool, multipart,
AST_VECTOR_GET(&body_parts, i)->part);
2407 free_body_parts(&body_parts);
2418 static pjsip_msg_body *generate_notify_body(pj_pool_t *pool,
struct ast_sip_subscription *root,
2419 unsigned int force_full_state)
2421 pjsip_msg_body *body;
2432 pj_cstr(&type, ast_sip_subscription_get_body_type(root));
2433 pj_cstr(&subtype, ast_sip_subscription_get_body_subtype(root));
2436 body = pjsip_msg_body_create(pool, &type, &subtype, &text);
2442 body = generate_list_body(pool, root, force_full_state);
2451 static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
2453 pjsip_require_hdr *require;
2455 require = pjsip_require_hdr_create(pool);
2456 pj_strdup2(pool, &require->values[0],
"eventlist");
2462 static void set_state_terminated(
struct ast_sip_subscription *sub)
2484 pjsip_evsub *evsub = sub_tree->
evsub;
2485 pjsip_tx_data *tdata;
2494 NULL, NULL, &tdata) != PJ_SUCCESS) {
2498 tdata->msg->body = generate_notify_body(tdata->pool, sub_tree->
root, force_full_state);
2499 if (!tdata->msg->body) {
2500 pjsip_tx_data_dec_ref(tdata);
2505 pjsip_require_hdr *require = create_require_eventlist(tdata->pool);
2506 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
2515 if (sip_subscription_send_request(sub_tree, tdata)) {
2525 static int serialized_send_notify(
void *userdata)
2528 pjsip_dialog *dlg = sub_tree->
dlg;
2530 pjsip_dlg_inc_lock(dlg);
2541 if (sub_tree->
state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS
2543 pjsip_dlg_dec_lock(dlg);
2544 ao2_cleanup(sub_tree);
2549 sub_tree->
state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
2552 send_notify(sub_tree, 0);
2555 sub_tree->
state == SIP_SUB_TREE_TERMINATED
2556 ?
"SUBSCRIPTION_TERMINATED" :
"SUBSCRIPTION_STATE_CHANGED",
2559 pjsip_dlg_dec_lock(dlg);
2560 ao2_cleanup(sub_tree);
2564 static int sched_cb(
const void *data)
2570 ao2_cleanup(sub_tree);
2586 ao2_cleanup(sub_tree);
2597 pjsip_dialog *dlg = sub->
tree->
dlg;
2599 pjsip_dlg_inc_lock(dlg);
2601 if (sub->
tree->
state != SIP_SUB_TREE_NORMAL) {
2602 pjsip_dlg_dec_lock(dlg);
2606 if (ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub),
2607 ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->
body_text)) {
2608 pjsip_dlg_dec_lock(dlg);
2615 sub->
tree->
state = SIP_SUB_TREE_TERMINATE_PENDING;
2619 res = schedule_notification(sub->
tree);
2624 sub->
tree->
state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
2626 res = send_notify(sub->
tree, 0);
2633 pjsip_dlg_dec_lock(dlg);
2637 pjsip_sip_uri *ast_sip_subscription_get_sip_uri(
struct ast_sip_subscription *sub)
2642 void ast_sip_subscription_get_local_uri(
struct ast_sip_subscription *sub,
char *buf,
size_t size)
2644 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->
uri, buf, size);
2647 void ast_sip_subscription_get_remote_uri(
struct ast_sip_subscription *sub,
char *buf,
size_t size)
2653 uri = pjsip_uri_get_uri(dlg->remote.info->uri);
2655 if (pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, uri, buf, size) < 0) {
2660 const char *ast_sip_subscription_get_resource_name(
struct ast_sip_subscription *sub)
2665 int ast_sip_subscription_is_terminated(
const struct ast_sip_subscription *sub)
2670 static int sip_subscription_accept(
struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata,
int response)
2675 if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) {
2679 pj_list_init(&res_hdr);
2682 pj_list_insert_before(&res_hdr, create_require_eventlist(rdata->tp_info.pool));
2685 return pjsip_evsub_accept(sub_tree->
evsub, rdata, response, &res_hdr) == PJ_SUCCESS ? 0 : -1;
2693 int ast_sip_subscription_add_datastore(
struct ast_sip_subscription *subscription,
struct ast_datastore *datastore)
2698 struct ast_datastore *ast_sip_subscription_get_datastore(
struct ast_sip_subscription *subscription,
const char *name)
2703 void ast_sip_subscription_remove_datastore(
struct ast_sip_subscription *subscription,
const char *name)
2708 struct ao2_container *ast_sip_subscription_get_datastores(
const struct ast_sip_subscription *subscription)
2723 void ast_sip_publication_remove_datastore(
struct ast_sip_publication *publication,
const char *name)
2733 void ast_sip_subscription_set_persistence_data(
struct ast_sip_subscription *subscription,
struct ast_json *persistence_data)
2750 const struct ast_json *ast_sip_subscription_get_persistence_data(
const struct ast_sip_subscription *subscription)
2757 static int publication_hash_fn(
const void *obj,
const int flags)
2765 static int publication_cmp_fn(
void *obj,
void *arg,
int flags)
2785 ast_log(LOG_ERROR,
"No event package specified for publish handler. Cannot register\n");
2790 PUBLICATIONS_BUCKETS, publication_hash_fn, NULL, publication_cmp_fn);
2792 ast_log(LOG_ERROR,
"Could not allocate publications container for event '%s'\n",
2797 publish_add_handler(handler);
2808 if (handler == iter) {
2809 AST_RWLIST_REMOVE_CURRENT(next);
2814 AST_RWLIST_TRAVERSE_SAFE_END;
2844 pj_str_t accept[AST_SIP_MAX_ACCEPT] = { {0, }, };
2849 ast_log(LOG_ERROR,
"No event package specified for subscription handler. Cannot register\n");
2853 existing = find_sub_handler_for_event_name(handler->
event_name);
2856 "Unable to register subscription handler for event %s. A handler is already registered\n",
2861 for (i = 0; i < AST_SIP_MAX_ACCEPT && !ast_strlen_zero(handler->
accept[i]); ++i) {
2862 pj_cstr(&accept[i], handler->
accept[i]);
2867 pjsip_evsub_register_pkg(&pubsub_module, &event, DEFAULT_EXPIRES, i, accept);
2869 sub_add_handler(handler);
2880 if (handler == iter) {
2881 AST_RWLIST_REMOVE_CURRENT(next);
2885 AST_RWLIST_TRAVERSE_SAFE_END;
2894 if (!strcmp(gen->
type, type)
2895 && !strcmp(gen->
subtype, subtype)) {
2908 gen = find_body_generator_type_subtype_nolock(type, subtype);
2916 char *subtype = accept_copy;
2917 char *type = strsep(&subtype,
"/");
2919 if (ast_strlen_zero(type) || ast_strlen_zero(subtype)) {
2923 return find_body_generator_type_subtype(type, subtype);
2927 size_t num_accept,
const char *
body_type)
2932 for (i = 0; i < num_accept; ++i) {
2933 generator = find_body_generator_accept(accept[i]);
2935 ast_debug(3,
"Body generator %p found for accept type %s\n", generator, accept[i]);
2936 if (strcmp(generator->
body_type, body_type)) {
2937 ast_log(LOG_WARNING,
"Body generator '%s/%s'(%p) does not accept the type of data this event generates\n",
2944 ast_debug(3,
"No body generator found for accept type %s\n", accept[i]);
2978 ast_debug(3,
"No notify data, not generating any body content\n");
2984 res = ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub),
2985 ast_sip_subscription_get_body_subtype(sub), &data, &sub->
body_text);
2987 ao2_cleanup(notify_data);
2992 static int pubsub_on_refresh_timeout(
void *userdata);
2994 static int initial_notify_task(
void * obj)
2999 pjsip_evsub_terminate(ind->sub_tree->
evsub, PJ_TRUE);
3001 send_notify(ind->sub_tree, 1);
3007 if (ind->expires != PJSIP_EXPIRES_NOT_SPECIFIED) {
3012 ind->sub_tree->
dlg->call_id->id.slen + 1);
3016 (
int)ind->sub_tree->
dlg->call_id->id.slen, ind->sub_tree->
dlg->call_id->id.ptr);
3018 ast_debug(3,
"Scheduling timer: %s\n", name);
3020 ind->expires * 1000, pubsub_on_refresh_timeout, name,
3023 ast_log(LOG_ERROR,
"Unable to create expiration timer of %d seconds for %s\n",
3024 ind->expires, name);
3034 static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
3036 pjsip_expires_hdr *expires_header;
3042 pjsip_uri *request_uri;
3043 size_t resource_size;
3046 pj_status_t dlg_status;
3047 const pj_str_t *user;
3049 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3050 ast_assert(endpoint != NULL);
3054 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 603, NULL, NULL, NULL);
3058 request_uri = rdata->msg_info.msg->line.req.uri;
3060 if (!ast_sip_is_uri_sip_sips(request_uri)) {
3061 char uri_str[PJSIP_MAX_URL_SIZE];
3063 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str,
sizeof(uri_str));
3064 ast_log(LOG_WARNING,
"Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
3065 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
3069 user = ast_sip_pjsip_uri_get_username(request_uri);
3070 resource_size = pj_strlen(user) + 1;
3072 ast_copy_pj_str(resource, user, resource_size);
3078 AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(resource);
3080 expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
3081 if (expires_header) {
3082 if (expires_header->ivalue == 0) {
3083 ast_debug(1,
"Subscription request from endpoint %s rejected. Expiration of 0 is invalid\n",
3085 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
3089 ast_log(LOG_WARNING,
"Subscription expiration %d is too brief for endpoint %s. Minimum is %u\n",
3091 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 423, NULL, NULL, NULL);
3098 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3102 generator = subscription_get_generator_from_rdata(rdata, handler);
3104 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3108 memset(&tree, 0,
sizeof(tree));
3109 resp = build_resource_tree(endpoint, handler, resource, &tree,
3110 ast_sip_pubsub_has_eventlist_support(rdata), rdata);
3111 if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
3112 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
3113 resource_tree_destroy(&tree);
3117 sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
3119 if (dlg_status != PJ_EEXISTS) {
3120 ast_debug(3,
"No dialog exists, rejecting\n");
3121 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
3127 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
3128 resource_tree_destroy(&tree);
3132 ind->sub_tree =
ao2_bump(sub_tree);
3134 ind->expires = PJSIP_EXPIRES_NOT_SPECIFIED;
3136 sub_tree->
persistence = subscription_persistence_create(sub_tree);
3137 subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_CREATED);
3138 sip_subscription_accept(sub_tree, rdata, resp);
3140 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
3146 resource_tree_destroy(&tree);
3168 static enum sip_publish_type determine_sip_publish_type(pjsip_rx_data *rdata,
3169 pjsip_generic_string_hdr *etag_hdr,
unsigned int *expires,
int *entity_id)
3171 pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
3174 char etag[pj_strlen(&etag_hdr->hvalue) + 1];
3176 ast_copy_pj_str(etag, &etag_hdr->hvalue,
sizeof(etag));
3178 if (sscanf(etag,
"%30d", entity_id) != 1) {
3179 return SIP_PUBLISH_UNKNOWN;
3183 *expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
3186 return SIP_PUBLISH_REMOVE;
3187 }
else if (!etag_hdr && rdata->msg_info.msg->body) {
3188 return SIP_PUBLISH_INITIAL;
3189 }
else if (etag_hdr && !rdata->msg_info.msg->body) {
3190 return SIP_PUBLISH_REFRESH;
3191 }
else if (etag_hdr && rdata->msg_info.msg->body) {
3192 return SIP_PUBLISH_MODIFY;
3195 return SIP_PUBLISH_UNKNOWN;
3199 static void publication_destroy_fn(
void *obj)
3203 ast_debug(3,
"Destroying SIP publication\n");
3206 ao2_cleanup(publication->
endpoint);
3215 pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
3216 size_t resource_len = strlen(resource) + 1, event_configuration_name_len = strlen(event_configuration_name) + 1;
3219 ast_assert(endpoint != NULL);
3221 if (!(publication = ao2_alloc(
sizeof(*publication) + resource_len + event_configuration_name_len, publication_destroy_fn))) {
3235 publication->
expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
3237 dst = publication->
data;
3238 publication->
resource = strcpy(dst, resource);
3239 dst += resource_len;
3246 pjsip_rx_data *rdata)
3248 pjsip_tx_data *tdata;
3249 pjsip_transaction *tsx;
3251 if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, &tdata) != PJ_SUCCESS) {
3255 if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) {
3258 snprintf(buf,
sizeof(buf),
"%d", pub->
entity_tag);
3259 ast_sip_add_header(tdata,
"SIP-ETag", buf);
3261 snprintf(buf,
sizeof(buf),
"%d", pub->
expires);
3262 ast_sip_add_header(tdata,
"Expires", buf);
3265 if (pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx) != PJ_SUCCESS) {
3266 pjsip_tx_data_dec_ref(tdata);
3270 pjsip_tsx_recv_msg(tsx, rdata);
3272 if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) {
3273 pjsip_tx_data_dec_ref(tdata);
3284 char *resource_name;
3285 size_t resource_size;
3288 pjsip_uri *request_uri;
3290 const pj_str_t *user;
3292 request_uri = rdata->msg_info.msg->line.req.uri;
3294 if (!ast_sip_is_uri_sip_sips(request_uri)) {
3295 char uri_str[PJSIP_MAX_URL_SIZE];
3297 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str,
sizeof(uri_str));
3298 ast_log(LOG_WARNING,
"Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
3299 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
3303 user = ast_sip_pjsip_uri_get_username(request_uri);
3304 resource_size = pj_strlen(user) + 1;
3306 ast_copy_pj_str(resource_name, user, resource_size);
3312 AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(resource_name);
3316 ast_debug(1,
"No 'inbound-publication' defined for resource '%s'\n", resource_name);
3317 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
3322 ast_debug(1,
"Resource %s has a defined endpoint '%s', but does not match endpoint '%s' that received the request\n",
3324 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
3328 for (event_configuration_name = resource->events; event_configuration_name; event_configuration_name = event_configuration_name->
next) {
3329 if (!strcmp(event_configuration_name->
name, handler->
event_name)) {
3334 if (!event_configuration_name) {
3335 ast_debug(1,
"Event '%s' is not configured for '%s'\n", handler->
event_name, resource_name);
3336 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
3342 if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
3343 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
3347 publication = sip_create_publication(endpoint, rdata,
S_OR(resource_name,
""), event_configuration_name->
value);
3350 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
3354 publication->
handler = handler;
3356 AST_SIP_PUBLISH_STATE_INITIALIZED)) {
3357 ast_debug(3,
"Publication state change failed\n");
3358 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
3359 ao2_cleanup(publication);
3363 sip_publication_respond(publication, resp, rdata);
3368 static int publish_expire_callback(
void *data)
3379 static int publish_expire(
const void *data)
3387 ao2_cleanup(publication);
3393 static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
3395 pjsip_event_hdr *event_header;
3399 static const pj_str_t str_sip_if_match = {
"SIP-If-Match", 12 };
3400 pjsip_generic_string_hdr *etag_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_sip_if_match, NULL);
3401 enum sip_publish_type publish_type;
3403 unsigned int expires = 0;
3404 int entity_id, response = 0;
3406 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3407 ast_assert(endpoint != NULL);
3409 event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
3410 if (!event_header) {
3411 ast_log(LOG_WARNING,
"Incoming PUBLISH request from %s with no Event header\n",
3413 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3416 ast_copy_pj_str(event, &event_header->event_type,
sizeof(event));
3418 handler = find_pub_handler(event);
3420 ast_log(LOG_WARNING,
"No registered publish handler for event %s from %s\n", event,
3422 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3426 publish_type = determine_sip_publish_type(rdata, etag_hdr, &expires, &entity_id);
3429 if ((publish_type != SIP_PUBLISH_INITIAL) && (publish_type != SIP_PUBLISH_UNKNOWN)) {
3431 static const pj_str_t str_conditional_request_failed = {
"Conditional Request Failed", 26 };
3433 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 412, &str_conditional_request_failed,
3442 publication->
expires = expires;
3445 switch (publish_type) {
3446 case SIP_PUBLISH_INITIAL:
3447 publication = publish_request_initial(endpoint, rdata, handler);
3449 case SIP_PUBLISH_REFRESH:
3450 case SIP_PUBLISH_MODIFY:
3452 AST_SIP_PUBLISH_STATE_ACTIVE)) {
3458 case SIP_PUBLISH_REMOVE:
3460 AST_SIP_PUBLISH_STATE_TERMINATED);
3463 case SIP_PUBLISH_UNKNOWN:
3465 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
3481 sip_publication_respond(publication, response, rdata);
3497 const char *ast_sip_publication_get_event_configuration(
const struct ast_sip_publication *pub)
3502 int ast_sip_pubsub_is_body_generator_registered(
const char *type,
const char *subtype)
3504 return !!find_body_generator_type_subtype(type, subtype);
3511 pj_size_t accept_len;
3514 existing = find_body_generator_type_subtype_nolock(generator->
type, generator->
subtype);
3517 ast_log(LOG_WARNING,
"A body generator for %s/%s is already registered.\n",
3525 accept_len = strlen(generator->
type) + strlen(generator->
subtype) + 1;
3528 pj_strset(&accept,
ast_alloca(accept_len + 1), accept_len);
3529 sprintf((
char *) pj_strbuf(&accept),
"%s/%s", generator->
type, generator->
subtype);
3531 pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
3532 PJSIP_H_ACCEPT, NULL, 1, &accept);
3543 if (iter == generator) {
3548 AST_RWLIST_TRAVERSE_SAFE_END;
3567 if (iter == supplement) {
3572 AST_RWLIST_TRAVERSE_SAFE_END;
3576 const char *ast_sip_subscription_get_body_type(
struct ast_sip_subscription *sub)
3581 const char *ast_sip_subscription_get_body_subtype(
struct ast_sip_subscription *sub)
3586 int ast_sip_pubsub_generate_body_content(
const char *type,
const char *subtype,
3594 generator = find_body_generator_type_subtype(type, subtype);
3596 ast_log(LOG_WARNING,
"Unable to find a body generator for %s/%s\n",
3602 ast_log(LOG_WARNING,
"%s/%s body generator does not accept the type of data provided\n",
3609 ast_log(LOG_WARNING,
"%s/%s body generator could not to allocate a body\n",
3621 if (!strcmp(generator->
type, supplement->
type) &&
3644 int messages_waiting;
3645 int voice_messages_new;
3646 int voice_messages_old;
3647 int voice_messages_urgent_new;
3648 int voice_messages_urgent_old;
3649 char message_account[PJSIP_MAX_URL_SIZE];
3652 static int parse_simple_message_summary(
char *body,
3657 int found_counts = 0;
3659 if (ast_strlen_zero(body) || !summary) {
3664 memset(summary, 0,
sizeof(*summary));
3669 if (sscanf(line,
"voice-message: %d/%d (%d/%d)",
3670 &summary->voice_messages_new, &summary->voice_messages_old,
3671 &summary->voice_messages_urgent_new, &summary->voice_messages_urgent_old)) {
3674 sscanf(line,
"message-account: %s", summary->message_account);
3678 return !found_counts;
3681 static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
3685 const char *endpoint_name;
3692 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3694 ast_debug(1,
"Incoming MWI: Endpoint not found in rdata (%p)\n", rdata);
3700 ast_debug(1,
"Incoming MWI: Found endpoint: %s\n", endpoint_name);
3702 ast_debug(1,
"Incoming MWI: No incoming mailbox specified for endpoint '%s'\n", endpoint_name);
3704 "Endpoint: %s", endpoint_name);
3710 atsign = strchr(mailbox,
'@');
3712 ast_debug(1,
"Incoming MWI: No '@' found in endpoint %s's incoming mailbox '%s'. Can't parse context\n",
3719 context = atsign + 1;
3721 body =
ast_alloca(rdata->msg_info.msg->body->len + 1);
3722 rdata->msg_info.msg->body->print_body(rdata->msg_info.msg->body, body,
3723 rdata->msg_info.msg->body->len + 1);
3725 if (parse_simple_message_summary(body, &summary) != 0) {
3726 ast_debug(1,
"Incoming MWI: Endpoint: '%s' There was an issue getting message info from body '%s'\n",
3733 summary.voice_messages_new, summary.voice_messages_old)) {
3734 ast_log(LOG_ERROR,
"Incoming MWI: Endpoint: '%s' Could not publish MWI to stasis. "
3735 "Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
3737 summary.voice_messages_new, summary.voice_messages_old,
3738 summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
3741 ast_debug(1,
"Incoming MWI: Endpoint: '%s' Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
3743 summary.voice_messages_new, summary.voice_messages_old,
3744 summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
3748 "MessageAccount: %s\r\n"
3749 "VoiceMessagesNew: %d\r\n"
3750 "VoiceMessagesOld: %d\r\n"
3751 "VoiceMessagesUrgentNew: %d\r\n"
3752 "VoiceMessagesUrgentOld: %d",
3754 summary.voice_messages_new, summary.voice_messages_old,
3755 summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
3760 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, rc, NULL, NULL, NULL);
3764 static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
3766 if (rdata->msg_info.msg->body &&
3767 ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
3768 "application",
"simple-message-summary")) {
3769 return pubsub_on_rx_mwi_notify_request(rdata);
3774 static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
3776 if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
3777 return pubsub_on_rx_subscribe_request(rdata);
3778 }
else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_publish_method)) {
3779 return pubsub_on_rx_publish_request(rdata);
3780 }
else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_notify_method)) {
3781 return pubsub_on_rx_notify_request(rdata);
3869 static void clean_sub_tree(pjsip_evsub *evsub)
3873 sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
3875 ast_debug(3,
"Cleaning subscription %p\n", evsub);
3878 char task_name[256];
3881 ast_debug(3,
"Cancelling timer: %s\n", task_name);
3887 remove_subscription(sub_tree);
3889 pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
3891 #ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
3892 pjsip_evsub_dec_ref(sub_tree->
evsub);
3895 sub_tree->
evsub = NULL;
3900 subscription_persistence_remove(sub_tree);
3901 shutdown_subscriptions(sub_tree->
root);
3903 sub_tree->
state = SIP_SUB_TREE_TERMINATED;
3909 #if PJ_VERSION_NUM >= 0x020D0000
3910 # define HAVE_PJSIP_EVSUB_PENDING_NOTIFY 1
3922 static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
3925 pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
3927 ast_debug(3,
"evsub %p state %s event %s sub_tree %p sub_tree state %s\n", evsub,
3928 pjsip_evsub_get_state_name(evsub), pjsip_event_str(event->type), sub_tree,
3929 (sub_tree ? sub_tree_state_description[sub_tree->
state] :
"UNKNOWN"));
3931 if (!sub_tree || pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
3936 if (!(sub_tree->
state == SIP_SUB_TREE_TERMINATE_IN_PROGRESS
3937 || (event->type == PJSIP_EVENT_TSX_STATE && sub_tree->
state == SIP_SUB_TREE_NORMAL)
3943 #ifdef HAVE_PJSIP_EVSUB_PENDING_NOTIFY
3948 if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
3949 !pjsip_method_cmp(&event->body.tsx_state.tsx->method, &pjsip_subscribe_method) &&
3950 pjsip_evsub_get_expires(evsub) == 0) {
3951 ast_debug(3,
"Subscription ending, do nothing.\n");
3957 clean_sub_tree(evsub);
3960 static int pubsub_on_refresh_timeout(
void *userdata)
3963 pjsip_dialog *dlg = sub_tree->
dlg;
3965 ast_debug(3,
"sub_tree %p sub_tree state %s\n", sub_tree,
3966 (sub_tree ? sub_tree_state_description[sub_tree->
state] :
"UNKNOWN"));
3968 pjsip_dlg_inc_lock(dlg);
3969 if (sub_tree->
state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
3970 pjsip_dlg_dec_lock(dlg);
3974 if (sub_tree->
state == SIP_SUB_TREE_TERMINATE_PENDING) {
3975 sub_tree->
state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
3976 set_state_terminated(sub_tree->
root);
3982 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
3983 pjsip_dlg_dec_lock(dlg);
3988 send_notify(sub_tree, 1);
3991 "SUBSCRIPTION_TERMINATED" :
"SUBSCRIPTION_REFRESHED",
3994 pjsip_dlg_dec_lock(dlg);
3999 static int serialized_pubsub_on_refresh_timeout(
void *userdata)
4003 ast_debug(3,
"sub_tree %p sub_tree state %s\n", sub_tree,
4004 (sub_tree ? sub_tree_state_description[sub_tree->
state] :
"UNKNOWN"));
4006 pubsub_on_refresh_timeout(userdata);
4007 ao2_cleanup(sub_tree);
4021 static int cmp_strings(
char *s1,
char *s2)
4023 if (!ast_strlen_zero(s1) && !ast_strlen_zero(s2)) {
4024 return strcmp(s1, s2);
4027 return ast_strlen_zero(s1) == ast_strlen_zero(s2) ? 0 : 1;
4036 static int cmp_subscription_childrens(
struct ast_sip_subscription *s1,
struct ast_sip_subscription *s2)
4058 static int destroy_subscriptions_task(
void *obj)
4060 struct ast_sip_subscription *sub = (
struct ast_sip_subscription *) obj;
4075 static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
4076 int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
4081 sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
4082 ast_debug(3,
"evsub %p sub_tree %p sub_tree state %s\n", evsub, sub_tree,
4083 (sub_tree ? sub_tree_state_description[sub_tree->
state] :
"UNKNOWN"));
4085 if (!sub_tree || sub_tree->
state != SIP_SUB_TREE_NORMAL) {
4090 char task_name[256];
4093 ast_debug(3,
"Cancelling timer: %s\n", task_name);
4103 if (pjsip_evsub_get_state(sub_tree->
evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
4104 sub_tree->
state = SIP_SUB_TREE_TERMINATE_PENDING;
4107 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
4116 if (sub_tree->
state == SIP_SUB_TREE_NORMAL && sub_tree->
is_list) {
4119 struct ast_sip_subscription *old_root = sub_tree->
root;
4120 struct ast_sip_subscription *new_root = NULL;
4124 if (endpoint && (generator = subscription_get_generator_from_rdata(rdata, sub_tree->
root->
handler))) {
4129 memset(&tree, 0,
sizeof(tree));
4130 resp = build_resource_tree(endpoint, sub_tree->
root->
handler, resource, &tree,
4131 ast_sip_pubsub_has_eventlist_support(rdata), rdata);
4132 if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
4133 new_root = create_virtual_subscriptions(sub_tree->
root->
handler, resource, generator, sub_tree, tree.root);
4135 if (cmp_subscription_childrens(old_root, new_root)) {
4138 sub_tree->
root = new_root;
4148 set_state_terminated(old_root);
4152 shutdown_subscriptions(old_root);
4156 ast_log(LOG_ERROR,
"Failed to push task to destroy old subscriptions for RLS '%s->%s'.\n",
4164 sub_tree->
state = SIP_SUB_TREE_TERMINATE_PENDING;
4165 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
4168 resource_tree_destroy(&tree);
4172 subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_REFRESHED);
4174 #ifdef HAVE_PJSIP_EVSUB_PENDING_NOTIFY
4180 pubsub_on_refresh_timeout(sub_tree);
4184 ast_log(LOG_ERROR,
"Failed to push task to send NOTIFY.\n");
4185 sub_tree->
state = SIP_SUB_TREE_NORMAL;
4191 pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
4195 static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata,
int *p_st_code,
4196 pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
4198 struct ast_sip_subscription *sub;
4200 if (!(sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
4205 pjsip_evsub_get_state(evsub));
4208 static int serialized_pubsub_on_client_refresh(
void *userdata)
4211 pjsip_tx_data *tdata;
4213 if (!sub_tree->
evsub) {
4214 ao2_cleanup(sub_tree);
4218 if (pjsip_evsub_initiate(sub_tree->
evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
4219 pjsip_evsub_send_request(sub_tree->
evsub, tdata);
4221 pjsip_evsub_terminate(sub_tree->
evsub, PJ_TRUE);
4224 ao2_cleanup(sub_tree);
4228 static void pubsub_on_client_refresh(pjsip_evsub *evsub)
4232 if (!(sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
4237 ao2_cleanup(sub_tree);
4241 static void pubsub_on_server_timeout(pjsip_evsub *evsub)
4258 sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
4259 if (!sub_tree || sub_tree->
state != SIP_SUB_TREE_NORMAL) {
4263 sub_tree->
state = SIP_SUB_TREE_TERMINATE_PENDING;
4265 sub_tree->
state = SIP_SUB_TREE_NORMAL;
4266 ao2_cleanup(sub_tree);
4276 buf = ast_sip_create_ami_event(event, ami);
4281 sip_subscription_to_ami(sub_tree, &buf);
4291 return sub_tree->
role == AST_SIP_NOTIFIER ? ami_subscription_detail(
4292 sub_tree, arg,
"InboundSubscriptionDetail") : 0;
4297 return sub_tree->
role == AST_SIP_SUBSCRIBER ? ami_subscription_detail(
4298 sub_tree, arg,
"OutboundSubscriptionDetail") : 0;
4301 static int ami_show_subscriptions_inbound(
struct mansession *s,
const struct message *m)
4308 for_each_subscription(ami_subscription_detail_inbound, &ami);
4315 static int ami_show_subscriptions_outbound(
struct mansession *
s,
const struct message *
m)
4322 for_each_subscription(ami_subscription_detail_outbound, &ami);
4329 static int format_ami_resource_lists(
void *obj,
void *arg,
int flags)
4335 buf = ast_sip_create_ami_event(
"ResourceListDetail", ami);
4340 if (ast_sip_sorcery_object_to_ami(list, &buf)) {
4351 static int ami_show_resource_lists(
struct mansession *s,
const struct message *m)
4364 astman_send_listack(s, m,
"A listing of resource lists follows, presented as ResourceListDetail events",
4374 #define AMI_SHOW_SUBSCRIPTIONS_INBOUND "PJSIPShowSubscriptionsInbound"
4375 #define AMI_SHOW_SUBSCRIPTIONS_OUTBOUND "PJSIPShowSubscriptionsOutbound"
4377 #define MAX_REGEX_ERROR_LEN 128
4403 if (!sub_tree->
dlg) {
4407 callid = &sub_tree->
dlg->call_id->id;
4408 if (cli->wordlen <= pj_strlen(callid)
4409 && !strncasecmp(cli->a->word, pj_strbuf(callid), cli->wordlen)
4410 && (++cli->which > cli->a->n)) {
4413 ast_copy_pj_str(cli->
callid, callid, pj_strlen(callid) + 1);
4422 return sub_tree->
role == AST_SIP_NOTIFIER
4423 ? cli_complete_subscription_common(sub_tree, arg) : 0;
4428 return sub_tree->
role == AST_SIP_SUBSCRIBER
4429 ? cli_complete_subscription_common(sub_tree, arg) : 0;
4432 static char *cli_complete_subscription_callid(
struct ast_cli_args *a)
4435 on_subscription_t on_subscription;
4441 if (!strcasecmp(a->argv[3],
"inbound")) {
4442 on_subscription = cli_complete_subscription_inbound;
4443 }
else if (!strcasecmp(a->argv[3],
"outbound")) {
4444 on_subscription = cli_complete_subscription_outbound;
4453 cli.wordlen = strlen(a->word);
4455 for_each_subscription(on_subscription, &cli);
4476 const char *callid = (
const char *) cli->
buf;
4477 pj_str_t *sub_callid;
4485 int key_filler_width;
4488 if (!sub_tree->
dlg) {
4491 sub_callid = &sub_tree->
dlg->call_id->id;
4492 if (pj_strcmp2(sub_callid, callid)) {
4503 "===========================================================================\n",
4504 "ParameterName",
"ParameterValue");
4508 ast_str_append(&buf, 0,
"Expiry: %u\n", cli_subscription_expiry(sub_tree));
4510 sip_subscription_to_ami(sub_tree, &buf);
4531 value = strchr(key,
':');
4535 value_end = strchr(value,
'\n');
4541 key_len = value - key;
4542 key_filler_width = 20 - key_len;
4543 if (key_filler_width < 0) {
4544 key_filler_width = 0;
4546 value_len = value_end - value;
4548 ast_cli(cli->
a->fd,
"%.*s%*s%.*s\n",
4549 key_len, key, key_filler_width,
"",
4552 key = value_end + 1;
4554 ast_cli(cli->
a->fd,
"\n");
4563 return sub_tree->
role == AST_SIP_NOTIFIER
4564 ? cli_show_subscription_common(sub_tree, arg) : 0;
4569 return sub_tree->
role == AST_SIP_SUBSCRIBER
4570 ? cli_show_subscription_common(sub_tree, arg) : 0;
4575 on_subscription_t on_subscription;
4580 e->
command =
"pjsip show subscription {inbound|outbound}";
4581 e->
usage =
"Usage:\n"
4582 " pjsip show subscription inbound <call-id>\n"
4583 " pjsip show subscription outbound <call-id>\n"
4584 " Show active subscription with the dialog call-id\n";
4587 return cli_complete_subscription_callid(a);
4591 return CLI_SHOWUSAGE;
4594 if (!strcasecmp(a->argv[3],
"inbound")) {
4595 on_subscription = cli_show_subscription_inbound;
4596 }
else if (!strcasecmp(a->argv[3],
"outbound")) {
4597 on_subscription = cli_show_subscription_outbound;
4607 cli.
buf = (
void *) a->argv[4];
4608 for_each_subscription(on_subscription, &cli);
4613 #define CLI_SHOW_SUB_FORMAT_HEADER \
4614 "Endpoint: <Endpoint/Caller-ID.............................................>\n" \
4615 "Resource: <Resource/Event.................................................>\n" \
4616 " Expiry: <Expiry> <Call-id..............................................>\n" \
4617 "===========================================================================\n\n"
4618 #define CLI_SHOW_SUB_FORMAT_ENTRY \
4619 "Endpoint: %s/%s\n" \
4620 "Resource: %s/%s\n" \
4621 " Expiry: %8d %s\n\n"
4625 char caller_id[256];
4628 ast_callerid_merge(caller_id,
sizeof(caller_id),
4636 if (sub_tree->
dlg) {
4637 ast_copy_pj_str(callid, &sub_tree->
dlg->call_id->id,
sizeof(callid));
4645 cli_subscription_expiry(sub_tree), callid);
4662 return sub_tree->
role == AST_SIP_NOTIFIER
4663 ? cli_show_subscriptions_detail(sub_tree, arg) : 0;
4668 return sub_tree->
role == AST_SIP_SUBSCRIBER
4669 ? cli_show_subscriptions_detail(sub_tree, arg) : 0;
4674 on_subscription_t on_subscription;
4681 e->
command =
"pjsip show subscriptions {inbound|outbound} [like]";
4682 e->
usage =
"Usage:\n"
4683 " pjsip show subscriptions inbound [like <regex>]\n"
4684 " Show active inbound subscriptions\n"
4685 " pjsip show subscriptions outbound [like <regex>]\n"
4686 " Show active outbound subscriptions\n"
4688 " The regex selects a subscriptions output that matches.\n"
4689 " i.e., All output lines for a subscription are checked\n"
4690 " as a block by the regex.\n";
4696 if (a->argc != 4 && a->argc != 6) {
4697 return CLI_SHOWUSAGE;
4699 if (!strcasecmp(a->argv[3],
"inbound")) {
4700 on_subscription = cli_show_subscriptions_inbound;
4701 }
else if (!strcasecmp(a->argv[3],
"outbound")) {
4702 on_subscription = cli_show_subscriptions_outbound;
4706 return CLI_SHOWUSAGE;
4711 if (strcasecmp(a->argv[4],
"like")) {
4712 return CLI_SHOWUSAGE;
4716 memset(&like, 0,
sizeof(like));
4719 rc = regcomp(cli.
like, regex, REG_EXTENDED | REG_NOSUB);
4721 char *regerr =
ast_alloca(MAX_REGEX_ERROR_LEN);
4723 regerror(rc, cli.
like, regerr, MAX_REGEX_ERROR_LEN);
4724 ast_cli(a->fd,
"Regular expression '%s' failed to compile: %s\n",
4744 ast_cli(a->fd, CLI_SHOW_SUB_FORMAT_HEADER);
4745 for_each_subscription(on_subscription, &cli);
4746 ast_cli(a->fd,
"%d active subscriptions%s%s%s\n",
4748 regex ?
" matched \"" :
"",
4760 #define CLI_LIST_SUB_FORMAT_HEADER "%-30.30s %-30.30s %6.6s %s\n"
4761 #define CLI_LIST_SUB_FORMAT_ENTRY "%-30.30s %-30.30s %6d %s\n"
4765 char ep_cid_buf[50];
4766 char res_evt_buf[50];
4770 snprintf(ep_cid_buf,
sizeof(ep_cid_buf),
"%s/%s",
4777 snprintf(res_evt_buf,
sizeof(res_evt_buf),
"%s/%s",
4782 if (sub_tree->
dlg) {
4783 ast_copy_pj_str(callid, &sub_tree->
dlg->call_id->id,
sizeof(callid));
4791 cli_subscription_expiry(sub_tree),
4809 return sub_tree->
role == AST_SIP_NOTIFIER
4810 ? cli_list_subscriptions_detail(sub_tree, arg) : 0;
4815 return sub_tree->
role == AST_SIP_SUBSCRIBER
4816 ? cli_list_subscriptions_detail(sub_tree, arg) : 0;
4821 on_subscription_t on_subscription;
4828 e->
command =
"pjsip list subscriptions {inbound|outbound} [like]";
4829 e->
usage =
"Usage:\n"
4830 " pjsip list subscriptions inbound [like <regex>]\n"
4831 " List active inbound subscriptions\n"
4832 " pjsip list subscriptions outbound [like <regex>]\n"
4833 " List active outbound subscriptions\n"
4835 " The regex selects output lines that match.\n";
4841 if (a->argc != 4 && a->argc != 6) {
4842 return CLI_SHOWUSAGE;
4844 if (!strcasecmp(a->argv[3],
"inbound")) {
4845 on_subscription = cli_list_subscriptions_inbound;
4846 }
else if (!strcasecmp(a->argv[3],
"outbound")) {
4847 on_subscription = cli_list_subscriptions_outbound;
4851 return CLI_SHOWUSAGE;
4856 if (strcasecmp(a->argv[4],
"like")) {
4857 return CLI_SHOWUSAGE;
4861 memset(&like, 0,
sizeof(like));
4864 rc = regcomp(cli.
like, regex, REG_EXTENDED | REG_NOSUB);
4866 char *regerr =
ast_alloca(MAX_REGEX_ERROR_LEN);
4868 regerror(rc, cli.
like, regerr, MAX_REGEX_ERROR_LEN);
4869 ast_cli(a->fd,
"Regular expression '%s' failed to compile: %s\n",
4889 ast_cli(a->fd, CLI_LIST_SUB_FORMAT_HEADER,
4890 "Endpoint/CLI",
"Resource/Event",
"Expiry",
"Call-id");
4891 for_each_subscription(on_subscription, &cli);
4892 ast_cli(a->fd,
"\n%d active subscriptions%s%s%s\n",
4894 regex ?
" matched \"" :
"",
4907 AST_CLI_DEFINE(cli_list_subscriptions_inout,
"List active inbound/outbound subscriptions"),
4908 AST_CLI_DEFINE(cli_show_subscription_inout,
"Show active subscription details"),
4909 AST_CLI_DEFINE(cli_show_subscriptions_inout,
"Show active inbound/outbound subscriptions"),
4912 static int persistence_endpoint_str2struct(
const struct aco_option *opt,
struct ast_variable *var,
void *obj)
4920 static int persistence_endpoint_struct2str(
const void *obj,
const intptr_t *args,
char **buf)
4936 static int persistence_tag_struct2str(
const void *obj,
const intptr_t *args,
char **buf)
4944 static int persistence_generator_data_str2struct(
const struct aco_option *opt,
struct ast_variable *var,
void *obj)
4957 static int persistence_generator_data_struct2str(
const void *obj,
const intptr_t *args,
char **buf)
4977 static int persistence_expires_str2struct(
const struct aco_option *opt,
struct ast_variable *var,
void *obj)
4983 static int persistence_expires_struct2str(
const void *obj,
const intptr_t *args,
char **buf)
4986 char secs[AST_TIME_T_LEN];
4993 #define RESOURCE_LIST_INIT_SIZE 4
4995 static void resource_list_destructor(
void *obj)
5007 static void *resource_list_alloc(
const char *name)
5024 static int item_in_vector(
const struct resource_list *list,
const char *item)
5037 static int list_item_handler(
const struct aco_option *opt,
5044 while ((item =
ast_strip(strsep(&items,
",")))) {
5045 if (ast_strlen_zero(item)) {
5049 if (item_in_vector(list, item)) {
5050 ast_log(LOG_WARNING,
"Ignoring duplicated list item '%s'\n", item);
5064 static int list_item_to_str(
const void *obj,
const intptr_t *args,
char **buf)
5081 static int resource_list_apply_handler(
const struct ast_sorcery *sorcery,
void *obj)
5085 if (ast_strlen_zero(list->
event)) {
5086 ast_log(LOG_WARNING,
"Resource list '%s' has no event set\n",
5092 ast_log(LOG_WARNING,
"Resource list '%s' has no list items\n",
5100 static int apply_list_configuration(
struct ast_sorcery *sorcery)
5102 ast_sorcery_apply_default(sorcery,
"resource_list",
"config",
5103 "pjsip.conf,criteria=type=resource_list");
5105 NULL, resource_list_apply_handler)) {
5118 "", list_item_handler, list_item_to_str, NULL, 0, 0);
5127 #ifdef TEST_FRAMEWORK
5134 const char *bad_resources[] = {
5146 static int test_new_subscribe(
struct ast_sip_endpoint *endpoint,
const char *resource)
5150 for (i = 0; i < ARRAY_LEN(bad_resources); ++i) {
5151 if (!strcmp(resource, bad_resources[i])) {
5175 .notifier = &test_notifier,
5188 static int populate_list(
struct resource_list *list,
const char *event,
const char **
resources,
size_t num_resources)
5194 for (i = 0; i < num_resources; ++i) {
5208 static void cleanup_resource_list(
struct resource_list *list)
5230 const char *list_name,
const char *event,
const char **resources,
size_t num_resources)
5236 ast_test_status_update(test,
"Could not allocate resource list in sorcery\n");
5241 ast_test_status_update(test,
"Could not store the resource list in sorcery\n");
5246 if (populate_list(list, event, resources, num_resources)) {
5247 ast_test_status_update(test,
"Could not add resources to the resource list\n");
5248 cleanup_resource_list(list);
5268 static int check_node(
struct ast_test *test,
struct tree_node *node,
5269 const char **resources,
size_t num_resources)
5274 ast_test_status_update(test,
"Unexpected number of resources in tree. Expected %zu, got %zu\n",
5279 for (i = 0; i < num_resources; ++i) {
5280 if (strcmp(resources[i],
AST_VECTOR_GET(&node->children, i)->resource)) {
5281 ast_test_status_update(test,
"Mismatched resources. Expected '%s' but got '%s'\n",
5293 static void test_resource_tree_destroy(
struct resource_tree *tree)
5295 resource_tree_destroy(tree);
5299 static int ineligible_configuration(
void)
5310 value = ast_variable_retrieve(config,
"res_pjsip_pubsub",
"resource_list");
5311 if (ast_strlen_zero(value)) {
5316 if (strcasecmp(value,
"memory") && strcasecmp(value,
"astdb")) {
5328 const char *resources[] = {
5337 info->name =
"resource_tree";
5338 info->category =
"/res/res_pjsip_pubsub/";
5339 info->summary =
"Basic resource tree integrity check";
5341 "Create a resource list and ensure that our attempt to build a tree works as expected.";
5342 return AST_TEST_NOT_RUN;
5347 if (ineligible_configuration()) {
5348 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5349 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5350 return AST_TEST_NOT_RUN;
5353 list = create_resource_list(test,
"foo",
"test", resources, ARRAY_LEN(resources));
5355 return AST_TEST_FAIL;
5359 resp = build_resource_tree(NULL, &test_handler,
"foo", tree, 1, NULL);
5361 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5362 return AST_TEST_FAIL;
5366 ast_test_status_update(test,
"Resource tree has no root\n");
5367 return AST_TEST_FAIL;
5370 if (check_node(test, tree->root, resources, ARRAY_LEN(resources))) {
5371 return AST_TEST_FAIL;
5374 return AST_TEST_PASS;
5382 const char *resources_1[] = {
5388 const char *resources_2[] = {
5402 info->name =
"complex_resource_tree";
5403 info->category =
"/res/res_pjsip_pubsub/";
5404 info->summary =
"Complex resource tree integrity check";
5406 "Create a complex resource list and ensure that our attempt to build a tree works as expected.";
5407 return AST_TEST_NOT_RUN;
5412 if (ineligible_configuration()) {
5413 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5414 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5415 return AST_TEST_NOT_RUN;
5418 list_1 = create_resource_list(test,
"foo",
"test", resources_1, ARRAY_LEN(resources_1));
5420 return AST_TEST_FAIL;
5423 list_2 = create_resource_list(test,
"dwarves",
"test", resources_2, ARRAY_LEN(resources_2));
5425 return AST_TEST_FAIL;
5429 resp = build_resource_tree(NULL, &test_handler,
"foo", tree, 1, NULL);
5431 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5432 return AST_TEST_FAIL;
5436 ast_test_status_update(test,
"Resource tree has no root\n");
5437 return AST_TEST_FAIL;
5441 if (check_node(test, node, resources_1, ARRAY_LEN(resources_1))) {
5442 return AST_TEST_FAIL;
5447 if (check_node(test, node, resources_2, ARRAY_LEN(resources_2))) {
5448 return AST_TEST_FAIL;
5451 return AST_TEST_PASS;
5458 const char *resources[] = {
5468 info->name =
"bad_resource";
5469 info->category =
"/res/res_pjsip_pubsub/";
5470 info->summary =
"Ensure bad resources do not end up in the tree";
5472 "Create a resource list with a single bad resource. Ensure the bad resource does not end up in the tree.";
5473 return AST_TEST_NOT_RUN;
5478 if (ineligible_configuration()) {
5479 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5480 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5481 return AST_TEST_NOT_RUN;
5484 list = create_resource_list(test,
"foo",
"test", resources, ARRAY_LEN(resources));
5486 return AST_TEST_FAIL;
5490 resp = build_resource_tree(NULL, &test_handler,
"foo", tree, 1, NULL);
5492 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5493 return AST_TEST_FAIL;
5497 ast_test_status_update(test,
"Resource tree has no root\n");
5498 return AST_TEST_FAIL;
5502 if (check_node(test, tree->root, resources, ARRAY_LEN(resources) - 1)) {
5503 return AST_TEST_FAIL;
5506 return AST_TEST_PASS;
5515 const char *resources_1[] = {
5522 const char *resources_2[] = {
5532 info->name =
"bad_branch";
5533 info->category =
"/res/res_pjsip_pubsub/";
5534 info->summary =
"Ensure bad branches are pruned from the tree";
5536 "Create a resource list that makes a tree with an entire branch of bad resources.\n"
5537 "Ensure the bad branch is pruned from the tree.";
5538 return AST_TEST_NOT_RUN;
5543 if (ineligible_configuration()) {
5544 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5545 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5546 return AST_TEST_NOT_RUN;
5549 list_1 = create_resource_list(test,
"foo",
"test", resources_1, ARRAY_LEN(resources_1));
5551 return AST_TEST_FAIL;
5553 list_2 = create_resource_list(test,
"gross",
"test", resources_2, ARRAY_LEN(resources_2));
5555 return AST_TEST_FAIL;
5559 resp = build_resource_tree(NULL, &test_handler,
"foo", tree, 1, NULL);
5561 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5562 return AST_TEST_FAIL;
5566 ast_test_status_update(test,
"Resource tree has no root\n");
5567 return AST_TEST_FAIL;
5573 if (check_node(test, tree->root, resources_1, ARRAY_LEN(resources_1) - 1)) {
5574 return AST_TEST_FAIL;
5577 return AST_TEST_PASS;
5586 const char *resources_1[] = {
5592 const char *resources_2[] = {
5605 info->name =
"duplicate_resource";
5606 info->category =
"/res/res_pjsip_pubsub/";
5607 info->summary =
"Ensure duplicated resources do not end up in the tree";
5609 "Create a resource list with a single duplicated resource. Ensure the duplicated resource does not end up in the tree.";
5610 return AST_TEST_NOT_RUN;
5615 if (ineligible_configuration()) {
5616 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5617 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5618 return AST_TEST_NOT_RUN;
5621 list_1 = create_resource_list(test,
"foo",
"test", resources_1, ARRAY_LEN(resources_1));
5623 return AST_TEST_FAIL;
5626 list_2 = create_resource_list(test,
"ducks",
"test", resources_2, ARRAY_LEN(resources_2));
5628 return AST_TEST_FAIL;
5632 resp = build_resource_tree(NULL, &test_handler,
"foo", tree, 1, NULL);
5634 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5635 return AST_TEST_FAIL;
5639 ast_test_status_update(test,
"Resource tree has no root\n");
5640 return AST_TEST_FAIL;
5647 if (check_node(test, node, resources_1, ARRAY_LEN(resources_1) - 2)) {
5648 return AST_TEST_FAIL;
5655 if (check_node(test, node, resources_2, ARRAY_LEN(resources_2) - 1)) {
5656 return AST_TEST_FAIL;
5659 return AST_TEST_PASS;
5667 const char *resources_1[] = {
5670 const char *resources_2[] = {
5677 info->name =
"loop";
5678 info->category =
"/res/res_pjsip_pubsub/";
5679 info->summary =
"Test that loops are properly detected.";
5681 "Create two resource lists that refer to each other. Ensure that attempting to build a tree\n"
5682 "results in an empty tree.";
5683 return AST_TEST_NOT_RUN;
5688 if (ineligible_configuration()) {
5689 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5690 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5691 return AST_TEST_NOT_RUN;
5694 list_1 = create_resource_list(test,
"herp",
"test", resources_1, ARRAY_LEN(resources_1));
5696 return AST_TEST_FAIL;
5698 list_2 = create_resource_list(test,
"derp",
"test", resources_2, ARRAY_LEN(resources_2));
5700 return AST_TEST_FAIL;
5704 resp = build_resource_tree(NULL, &test_handler,
"herp", tree, 1, NULL);
5706 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5707 return AST_TEST_FAIL;
5710 return AST_TEST_PASS;
5717 const char *resources[] = {
5726 info->name =
"bad_event";
5727 info->category =
"/res/res_pjsip_pubsub/";
5728 info->summary =
"Ensure that list with wrong event specified is not retrieved";
5730 "Create a simple resource list for event 'tsetse'. Ensure that trying to retrieve the list for event 'test' fails.";
5731 return AST_TEST_NOT_RUN;
5736 if (ineligible_configuration()) {
5737 ast_test_status_update(test,
"Ineligible configuration for this test. Please add a "
5738 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5739 return AST_TEST_NOT_RUN;
5742 list = create_resource_list(test,
"foo",
"tsetse", resources, ARRAY_LEN(resources));
5744 return AST_TEST_FAIL;
5751 resp = build_resource_tree(NULL, &test_handler,
"foo", tree, 1, NULL);
5753 ast_test_status_update(test,
"Unexpected response %d when building resource tree\n", resp);
5754 return AST_TEST_FAIL;
5758 ast_test_status_update(test,
"Resource tree has no root\n");
5759 return AST_TEST_FAIL;
5762 if (strcmp(tree->root->resource,
"foo")) {
5763 ast_test_status_update(test,
"Unexpected resource %s found in tree\n", tree->root->resource);
5764 return AST_TEST_FAIL;
5767 return AST_TEST_PASS;
5786 const char *
event = var->
name + 6;
5789 if (ast_strlen_zero(event) || ast_strlen_zero(var->
value)) {
5793 item = ast_variable_new(event, var->
value,
"");
5806 static int load_module(
void)
5808 static const pj_str_t str_PUBLISH = {
"PUBLISH", 7 };
5811 sorcery = ast_sip_get_sorcery();
5814 ast_log(LOG_ERROR,
"Could not create scheduler for publication expiration\n");
5819 ast_log(LOG_ERROR,
"Could not start scheduler thread for publication expiration\n");
5824 ast_sorcery_apply_config(sorcery,
"res_pjsip_pubsub");
5825 ast_sorcery_apply_default(sorcery,
"subscription_persistence",
"astdb",
"subscription_persistence");
5828 ast_log(LOG_ERROR,
"Could not register subscription persistence object support\n");
5847 persistence_endpoint_str2struct, persistence_endpoint_struct2str, NULL, 0, 0);
5849 persistence_tag_str2struct, persistence_tag_struct2str, NULL, 0, 0);
5851 persistence_expires_str2struct, persistence_expires_struct2str, NULL, 0, 0);
5857 persistence_generator_data_str2struct, persistence_generator_data_struct2str, NULL, 0, 0);
5859 if (apply_list_configuration(sorcery)) {
5864 ast_sorcery_apply_default(sorcery,
"inbound-publication",
"config",
"pjsip.conf,criteria=type=inbound-publication");
5867 ast_log(LOG_ERROR,
"Could not register subscription persistence object support\n");
5873 resource_endpoint_handler, NULL, NULL, 0, 0);
5877 if (ast_sip_register_service(&pubsub_module)) {
5878 ast_log(LOG_ERROR,
"Could not register pubsub service\n");
5883 if (pjsip_evsub_init_module(ast_sip_get_pjsip_endpoint()) != PJ_SUCCESS) {
5884 ast_log(LOG_ERROR,
"Could not initialize pjsip evsub module.\n");
5885 ast_sip_unregister_service(&pubsub_module);
5894 pjsip_media_type_init2(&rlmi_media_type,
"application",
"rlmi+xml");
5896 pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &str_PUBLISH);
5909 ami_show_subscriptions_inbound);
5911 ami_show_subscriptions_outbound);
5913 ami_show_resource_lists);
5918 AST_TEST_REGISTER(complex_resource_tree);
5919 AST_TEST_REGISTER(bad_resource);
5920 AST_TEST_REGISTER(bad_branch);
5921 AST_TEST_REGISTER(duplicate_resource);
5922 AST_TEST_REGISTER(loop);
5923 AST_TEST_REGISTER(bad_event);
5928 static int unload_module(
void)
5931 AST_TEST_UNREGISTER(complex_resource_tree);
5932 AST_TEST_UNREGISTER(bad_resource);
5933 AST_TEST_UNREGISTER(bad_branch);
5934 AST_TEST_UNREGISTER(duplicate_resource);
5935 AST_TEST_UNREGISTER(loop);
5936 AST_TEST_UNREGISTER(bad_event);
5946 ast_sip_unregister_service(&pubsub_module);
5954 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"PJSIP event resource",
5955 .support_level = AST_MODULE_SUPPORT_CORE,
5956 .load = load_module,
5957 .unload = unload_module,
5959 .requires =
"res_pjsip",
void(* to_string)(void *body, struct ast_str **str)
Convert the body to a string.
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
struct ast_variable * next
unsigned int generate_initial_notify
const char * event_name
The name of the event this handler deals with.
void *(* get_notify_data)(struct ast_sip_subscription *sub)
Supply data needed to create a NOTIFY body.
char * str
Subscriber phone number (Malloced)
char data[0]
Data containing the above.
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
struct ast_sip_subscription * root
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
void astman_append(struct mansession *s, const char *fmt,...)
Asterisk main include file. File version handling, generic pbx functions.
int(* notify_created)(struct ast_sip_subscription *sub, pjsip_tx_data *tdata)
Optional callback to execute before sending outgoing NOTIFY requests. Because res_pjsip_pubsub create...
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
struct ast_sip_subscriber * subscriber
struct sip_subscription_tree * tree
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
A multipart body part and meta-information.
const char * type
Content type In "plain/text", "plain" is the type.
struct ast_sip_endpoint * endpoint
int ast_time_t_to_string(time_t time, char *buf, size_t length)
Converts to a string representation of a time_t as decimal seconds since the epoch. Returns -1 on failure, zero otherwise.
Data used to create bodies for NOTIFY/PUBLISH requests.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_party_name name
Subscriber name.
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
Cancels the next invocation of a task.
void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer)
Set a serializer on a SIP dialog so requests and responses are automatically serialized.
Type declaration for container of body part structures.
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
descriptor for a cli entry.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
struct ast_sip_notifier * notifier
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
char local_name[PJ_INET6_ADDRSTRLEN]
const char * accept[AST_SIP_MAX_ACCEPT]
static void destroy_subscriptions(void)
Destroy the active Stasis subscriptions.
Party identification options for endpoints.
Structure for variables, used for configurations and for channel variables.
static pj_pool_t * pool
Global memory pool for configuration and timers.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
Set an endpoint on a SIP dialog so in-dialog requests do not undergo endpoint lookup.
struct ast_sip_subscription::@470 children
struct ast_str * body_text
Universally unique identifier support.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
struct ast_sip_endpoint_subscription_configuration subscription
Perform no matching, return all objects.
int ast_datastores_add(struct ao2_container *datastores, struct ast_datastore *datastore)
Add a data store to a container.
int(* new_publication)(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
Called when a PUBLISH to establish a new publication arrives.
int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *type, const char *regex, aco_option_handler config_handler, sorcery_fields_handler sorcery_handler)
Register a regex for multiple fields within an object.
#define CHARFLDSET(type, field)
A helper macro to pass the appropriate arguments to aco_option_register for OPT_CHAR_ARRAY_T.
Full structure for sorcery.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
Type for a default handler that should do nothing.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Structure for a data store type.
char * str
Subscriber name (Malloced)
#define AST_SCHED_DEL_UNREF(sched, id, refcall)
schedule task to get deleted and call unref function
#define SORCERY_OBJECT(details)
Macro which must be used at the beginning of each sorcery capable object.
Structure used for persisting an inbound subscription.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
const char * type
Content type In "plain/text", "plain" is the type.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Return all matching objects.
struct ast_json * ast_json_load_string(const char *input, struct ast_json_error *error)
Parse null terminated string into a JSON object or array.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
Structure for a data store object.
int(* get_resource_display_name)(struct ast_sip_endpoint *endpoint, const char *resource, char *display_name, int display_name_size)
Supply Display Name for resource.
void ast_sip_transport_monitor_unregister_key(const char *transport_key, ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a reliable transport shutdown monitor.
void(* to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf)
Converts the subscriber to AMI.
char packet[PJSIP_MAX_PKT_LEN]
struct subscription_persistence * persistence
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to SIP servants and wait for it to complete.
int ast_get_timeval(const char *src, struct timeval *tv, struct timeval _default, int *consumed)
Parse a time (float) string.
A node for a resource tree.
JSON parsing error information.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
enum ast_sip_subscription_role role
#define ast_publish_mwi_state(mailbox, context, new_msgs, old_msgs)
Publish a MWI state update via stasis.
A vector of strings commonly used throughout this module.
Resource list configuration item.
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
struct ast_sip_pubsub_body_generator * body_generator
struct ast_datastore * ast_datastores_find(struct ao2_container *datastores, const char *name)
Find a data store in a container.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
void(* publish_expire)(struct ast_sip_publication *pub)
Called when a publication has reached its expiration.
struct ao2_container * ast_datastores_alloc(void)
Allocate a specialized data stores container.
struct ao2_container * datastores
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Asterisk datastore objects.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Type for default option handler for character array strings.
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_key(const char *transport_key, ast_transport_monitor_shutdown_cb cb, void *ao2_data)
Register a reliable transport shutdown monitor callback.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Type for default option handler for bools (ast_true/ast_false)
struct ast_sip_sched_task * ast_sip_schedule_task(struct ast_taskprocessor *serializer, int interval, ast_sip_task sip_task, const char *name, void *task_data, enum ast_sip_scheduler_task_flags flags)
Schedule a task to run in the res_pjsip thread pool.
const char * subtype
Content subtype In "plain/text", "text" is the subtype.
struct stasis_message_type * ast_manager_get_generic_type(void)
Get the stasis_message_type for generic messages.
int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
Create and potentially persist an object using an available wizard.
#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags,...)
Register a field within an object with custom handlers.
#define ast_config_load(filename, flags)
Load a config file.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
struct ao2_container * datastores
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Type for default option handler for unsigned integers.
void *(* allocate_body)(void *data)
allocate body structure.
#define ast_strdupa(s)
duplicate a string in memory from the stack
int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to the serializer and wait for it to complete.
const struct ast_sip_subscription_handler * handler
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define AST_MAX_EXTENSION
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Scheduler Routines (derived from cheops)
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
const struct ast_datastore_info * info
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
struct ast_datastore * ast_datastores_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Allocate a datastore for use with the datastores container.
int(* subscription_established)(struct ast_sip_subscription *sub)
Called when an inbound subscription has been accepted.
A set of macros to manage forward-linked lists.
#define ast_malloc(len)
A wrapper for malloc()
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_VECTOR(name, type)
Define a vector structure.
An entity with which Asterisk communicates.
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
const char * subtype
Content subtype In "plain/text", "text" is the subtype.
static force_inline char * ast_str_to_lower(char *str)
Convert a string to all lower-case.
enum sip_subscription_tree_state state
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Structure representing a "virtual" SIP subscription.
#define ast_test_suite_event_notify(s, f,...)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
pjsip_evsub_state subscription_state
struct ast_sip_endpoint_id_configuration id
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
int(* publication_state_change)(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
Published resource has changed states.
int(* new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource)
Called when a SUBSCRIBE arrives attempting to establish a new subscription.
int ast_shutdown_final(void)
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
Delete an object.
unsigned int expires
Expiration time of the publication.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
struct ast_sip_publish_handler * handler
Handler for this publication.
Support for dynamic strings.
#define ao2_unlink(container, obj)
Remove an object from a container.
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
char * event_configuration_name
The name of the event type configuration.
Type for default option handler for bools (ast_true/ast_false)
char * ast_generate_random_string(char *buf, size_t size)
Create a pseudo-random string of a fixed length.
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
void(* destroy_body)(void *body)
Deallocate resources created for the body.
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Callbacks that publication handlers will define.
char * resource
The resource the publication is to.
struct ast_taskprocessor * ast_sip_get_distributor_serializer(pjsip_rx_data *rdata)
Determine the distributor serializer for the SIP message.
#define ast_module_ref(mod)
Hold a reference to the module.
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
char transport_key[IP6ADDR_COLON_PORT_BUFLEN]
struct ast_sip_endpoint * endpoint
The endpoint with which the subscription is communicating.
#define ast_calloc(num, len)
A wrapper for calloc()
struct ast_sip_sched_task * expiration_task
void(* state_change)(struct ast_sip_subscription *sub, pjsip_msg_body *body, enum pjsip_evsub_state state)
A NOTIFY has been received.
void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a transport shutdown monitor from all reliable transports.
void ast_datastores_remove(struct ao2_container *datastores, const char *name)
Remove a data store from a container.
pjsip_generic_string_hdr * cid
char * endpoint
Optional name of an endpoint that is only allowed to publish to this resource.
Module has failed to load, may be in an inconsistent state.
An API for managing task processing threads that can be shared across modules.
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
structure to hold users read from users.conf
struct ast_taskprocessor * serializer
Structure used to handle boolean flags.
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
#define ast_module_unref(mod)
Release a reference to the module.
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
int(* refresh_subscribe)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata)
Called when a SUBSCRIBE arrives for an already active subscription.
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
A ast_taskprocessor structure is a singleton by name.
const ast_string_field incoming_mwi_mailbox
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
char contact_uri[PJSIP_MAX_URL_SIZE]
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
unsigned int resource_display_name
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
#define AST_TEST_DEFINE(hdr)
Abstract JSON element (object, array, string, int, ...).
Structure representing a SIP publication.
int entity_tag
Entity tag for the publication.
pjsip_multipart_part * part
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
const char * display_name
unsigned char valid
TRUE if the name information is valid/present.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
int(* supplement_body)(void *body, void *data)
Add additional content to a SIP request body.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen)
Gets the task name.
const ast_string_field aors
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
#define ASTERISK_GPL_KEY
The text the key() function should return.
const char * default_accept
Default body type defined for the event package this notifier handles.
Structure representing a publication resource.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Asterisk module definitions.
struct ast_variable * events
Mapping for event types to configuration.
struct ast_json * persistence_data
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
unsigned int send_scheduled_notify
struct ao2_container * publications
Publications.
char src_name[PJ_INET6_ADDRSTRLEN]
unsigned char valid
TRUE if the number information is valid/present.
int(* generate_body_content)(void *body, void *data)
Add content to the body of a SIP request.
void(* subscription_shutdown)(struct ast_sip_subscription *subscription)
Called when a subscription is to be destroyed.
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
unsigned int notification_batch_interval
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
A tree of SIP subscriptions.
struct ast_json * generator_data
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
Update an object.
int sched_id
Scheduled item for expiration of publication.
char * ast_read_line_from_buffer(char **buffer)
Read lines from a string buffer.
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
unsigned int notification_batch_interval
struct ast_party_number number
Subscriber phone number.
#define ao2_link(container, obj)
Add an object to a container.