diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_pjsip_exten_state.c | 226 | ||||
-rw-r--r-- | res/res_pjsip_mwi.c | 293 | ||||
-rw-r--r-- | res/res_pjsip_pidf_body_generator.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_pubsub.c | 747 | ||||
-rw-r--r-- | res/res_pjsip_pubsub.exports.in | 7 | ||||
-rw-r--r-- | res/res_pjsip_xpidf_body_generator.c | 2 |
6 files changed, 669 insertions, 608 deletions
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c index f4bfef772..cb39026ca 100644 --- a/res/res_pjsip_exten_state.c +++ b/res/res_pjsip_exten_state.c @@ -68,26 +68,24 @@ struct exten_state_subscription { #define DEFAULT_PRESENCE_BODY "application/pidf+xml" static void subscription_shutdown(struct ast_sip_subscription *sub); -static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint, - pjsip_rx_data *rdata); -static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, - struct ast_sip_subscription_response_data *response_data); -static void subscription_timeout(struct ast_sip_subscription *sub); -static void subscription_terminated(struct ast_sip_subscription *sub, - pjsip_rx_data *rdata); +static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource); +static int notify_required(struct ast_sip_subscription *sub, + enum ast_sip_subscription_notify_reason reason); static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf); +struct ast_sip_notifier presence_notifier = { + .default_accept = DEFAULT_PRESENCE_BODY, + .new_subscribe = new_subscribe, + .notify_required = notify_required, +}; + struct ast_sip_subscription_handler presence_handler = { .event_name = "presence", .accept = { DEFAULT_PRESENCE_BODY, }, - .default_accept = DEFAULT_PRESENCE_BODY, .subscription_shutdown = subscription_shutdown, - .new_subscribe = new_subscribe, - .resubscribe = resubscribe, - .subscription_timeout = subscription_timeout, - .subscription_terminated = subscription_terminated, .to_ami = to_ami, + .notifier = &presence_notifier, }; static void exten_state_subscription_destructor(void *obj) @@ -98,14 +96,12 @@ static void exten_state_subscription_destructor(void *obj) ao2_cleanup(sub->sip_sub); } -static char *get_user_agent(pjsip_rx_data *rdata) +static char *get_user_agent(const struct ast_sip_subscription *sip_sub) { - static const pj_str_t USER_AGENT = { "User-Agent", 10 }; - size_t size; char *user_agent = NULL; - pjsip_user_agent_hdr *user_agent_hdr = pjsip_msg_find_hdr_by_name( - rdata->msg_info.msg, &USER_AGENT, NULL); + pjsip_user_agent_hdr *user_agent_hdr = ast_sip_subscription_get_header( + sip_sub, "User-Agent"); if (!user_agent_hdr) { return NULL; @@ -132,85 +128,30 @@ static char *get_user_agent(pjsip_rx_data *rdata) * sure that there are registered handler and provider objects available. */ static struct exten_state_subscription *exten_state_subscription_alloc( - struct ast_sip_endpoint *endpoint, enum ast_sip_subscription_role role, pjsip_rx_data *rdata) + struct ast_sip_subscription *sip_sub, struct ast_sip_endpoint *endpoint) { - RAII_VAR(struct exten_state_subscription *, exten_state_sub, - ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor), ao2_cleanup); + struct exten_state_subscription * exten_state_sub; + exten_state_sub = ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor); if (!exten_state_sub) { return NULL; } - if (!(exten_state_sub->sip_sub = ast_sip_create_subscription( - &presence_handler, role, endpoint, rdata))) { - ast_log(LOG_WARNING, "Unable to create SIP subscription for endpoint %s\n", - ast_sorcery_object_get_id(endpoint)); - return NULL; - } - + exten_state_sub->sip_sub = ao2_bump(sip_sub); exten_state_sub->last_exten_state = INITIAL_LAST_EXTEN_STATE; exten_state_sub->last_presence_state = AST_PRESENCE_NOT_SET; - exten_state_sub->user_agent = get_user_agent(rdata); - ao2_ref(exten_state_sub, +1); + exten_state_sub->user_agent = get_user_agent(sip_sub); return exten_state_sub; } /*! * \internal - * \brief Create and send a NOTIFY request to the subscriber. - */ -static void create_send_notify(struct exten_state_subscription *exten_state_sub, const char *reason, - pjsip_evsub_state evsub_state, struct ast_sip_exten_state_data *exten_state_data) -{ - RAII_VAR(struct ast_str *, body_text, ast_str_create(BODY_SIZE), ast_free_ptr); - pj_str_t reason_str; - const pj_str_t *reason_str_ptr = NULL; - pjsip_tx_data *tdata; - struct ast_sip_body body; - - body.type = ast_sip_subscription_get_body_type(exten_state_sub->sip_sub); - body.subtype = ast_sip_subscription_get_body_subtype(exten_state_sub->sip_sub); - - if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, - exten_state_data, &body_text)) { - ast_log(LOG_ERROR, "Unable to create body on NOTIFY request\n"); - return; - } - - body.body_text = ast_str_buffer(body_text); - - if (reason) { - pj_cstr(&reason_str, reason); - reason_str_ptr = &reason_str; - } - - if (pjsip_evsub_notify(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), - evsub_state, NULL, reason_str_ptr, &tdata) != PJ_SUCCESS) { - ast_log(LOG_WARNING, "Unable to create NOTIFY request\n"); - return; - } - - if (ast_sip_add_body(tdata, &body)) { - ast_log(LOG_WARNING, "Unable to add body to NOTIFY request\n"); - pjsip_tx_data_dec_ref(tdata); - return; - } - - if (ast_sip_subscription_send_request(exten_state_sub->sip_sub, tdata) != PJ_SUCCESS) { - ast_log(LOG_WARNING, "Unable to send NOTIFY request\n"); - } -} - -/*! - * \internal * \brief Get device state information and send notification to the subscriber. */ -static void send_notify(struct exten_state_subscription *exten_state_sub, const char *reason, - pjsip_evsub_state evsub_state) +static void send_notify(struct exten_state_subscription *exten_state_sub) { RAII_VAR(struct ao2_container*, info, NULL, ao2_cleanup); char *subtype = NULL, *message = NULL; - pjsip_dialog *dlg; struct ast_sip_exten_state_data exten_state_data = { .exten = exten_state_sub->exten, .presence_state = ast_hint_presence_state(NULL, exten_state_sub->context, @@ -220,11 +161,10 @@ static void send_notify(struct exten_state_subscription *exten_state_sub, const .user_agent = exten_state_sub->user_agent }; - dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub); - ast_copy_pj_str(exten_state_data.local, &dlg->local.info_str, - sizeof(exten_state_data.local)); - ast_copy_pj_str(exten_state_data.remote, &dlg->remote.info_str, - sizeof(exten_state_data.remote)); + ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub, + exten_state_data.local, sizeof(exten_state_data.local)); + ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub, + exten_state_data.remote, sizeof(exten_state_data.remote)); if ((exten_state_data.exten_state = ast_extension_state_extended( NULL, exten_state_sub->context, exten_state_sub->exten, &info)) < 0) { @@ -238,14 +178,14 @@ static void send_notify(struct exten_state_subscription *exten_state_sub, const "exten_state", 1024, 1024); exten_state_data.device_state_info = info; - create_send_notify(exten_state_sub, reason, evsub_state, &exten_state_data); + ast_sip_subscription_notify(exten_state_sub->sip_sub, &exten_state_data, 0); pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data.pool); } struct notify_task_data { struct ast_sip_exten_state_data exten_state_data; struct exten_state_subscription *exten_state_sub; - pjsip_evsub_state evsub_state; + int terminate; }; static void notify_task_data_destructor(void *obj) @@ -264,14 +204,12 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten { struct notify_task_data *task_data = ao2_alloc(sizeof(*task_data), notify_task_data_destructor); - struct pjsip_dialog *dlg; if (!task_data) { ast_log(LOG_WARNING, "Unable to create notify task data\n"); return NULL; } - task_data->evsub_state = PJSIP_EVSUB_STATE_ACTIVE; task_data->exten_state_sub = exten_state_sub; task_data->exten_state_sub->last_exten_state = info->exten_state; task_data->exten_state_sub->last_presence_state = info->presence_state; @@ -289,17 +227,16 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten ao2_ref(task_data->exten_state_data.device_state_info, +1); } - dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub); - ast_copy_pj_str(task_data->exten_state_data.local, &dlg->local.info_str, - sizeof(task_data->exten_state_data.local)); - ast_copy_pj_str(task_data->exten_state_data.remote, &dlg->remote.info_str, - sizeof(task_data->exten_state_data.remote)); + ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub, + task_data->exten_state_data.local, sizeof(task_data->exten_state_data.local)); + ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub, + task_data->exten_state_data.remote, sizeof(task_data->exten_state_data.remote)); if ((info->exten_state == AST_EXTENSION_DEACTIVATED) || (info->exten_state == AST_EXTENSION_REMOVED)) { - task_data->evsub_state = PJSIP_EVSUB_STATE_TERMINATED; ast_log(LOG_WARNING, "Watcher for hint %s %s\n", exten, info->exten_state == AST_EXTENSION_REMOVED ? "removed" : "deactivated"); + task_data->terminate = 1; } return task_data; @@ -313,9 +250,8 @@ static int notify_task(void *obj) task_data->exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "exten_state", 1024, 1024); - create_send_notify(task_data->exten_state_sub, task_data->evsub_state == - PJSIP_EVSUB_STATE_TERMINATED ? "noresource" : NULL, - task_data->evsub_state, &task_data->exten_state_data); + ast_sip_subscription_notify(task_data->exten_state_sub->sip_sub, &task_data->exten_state_data, + task_data->terminate); pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), task_data->exten_state_data.pool); @@ -407,24 +343,30 @@ static void subscription_shutdown(struct ast_sip_subscription *sub) ao2_cleanup(exten_state_sub); } -static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint, - pjsip_rx_data *rdata) +static int new_subscribe(struct ast_sip_endpoint *endpoint, + const char *resource) { - pjsip_uri *uri = rdata->msg_info.msg->line.req.uri; - pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri); - RAII_VAR(struct exten_state_subscription *, exten_state_sub, NULL, ao2_cleanup); - - if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) { - ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n"); - return NULL; + if (!ast_exists_extension(NULL, endpoint->context, resource, PRIORITY_HINT, NULL)) { + ast_log(LOG_WARNING, "Extension %s does not exist or has no associated hint\n", resource); + return 404; } - if (!(exten_state_sub = exten_state_subscription_alloc(endpoint, AST_SIP_NOTIFIER, rdata))) { - return NULL; + return 200; +} + +static int initial_subscribe(struct ast_sip_subscription *sip_sub) +{ + struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub); + const char *resource = ast_sip_subscription_get_resource_name(sip_sub); + struct exten_state_subscription *exten_state_sub; + + if (!(exten_state_sub = exten_state_subscription_alloc(sip_sub, endpoint))) { + ao2_cleanup(endpoint); + return -1; } ast_copy_string(exten_state_sub->context, endpoint->context, sizeof(exten_state_sub->context)); - ast_copy_pj_str(exten_state_sub->exten, &sip_uri->user, sizeof(exten_state_sub->exten)); + ast_copy_string(exten_state_sub->exten, resource, sizeof(exten_state_sub->exten)); if ((exten_state_sub->id = ast_extension_state_add_destroy_extended( exten_state_sub->context, exten_state_sub->exten, @@ -432,64 +374,50 @@ static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpo ast_log(LOG_WARNING, "Unable to subscribe endpoint '%s' to extension '%s@%s'\n", ast_sorcery_object_get_id(endpoint), exten_state_sub->exten, exten_state_sub->context); - pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE); - return NULL; + ao2_cleanup(endpoint); + ao2_cleanup(exten_state_sub); + return -1; } + /* Go ahead and cleanup the endpoint since we don't need it anymore */ + ao2_cleanup(endpoint); + /* bump the ref since ast_extension_state_add holds a reference */ ao2_ref(exten_state_sub, +1); if (add_datastore(exten_state_sub)) { ast_log(LOG_WARNING, "Unable to add to subscription datastore.\n"); - pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE); - return NULL; - } - - if (ast_sip_subscription_accept(exten_state_sub->sip_sub, rdata, 200)) { - ast_log(LOG_WARNING, "Unable to accept the incoming extension state subscription.\n"); - pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE); - return NULL; - } - - send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_ACTIVE); - return exten_state_sub->sip_sub; -} - -static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, - struct ast_sip_subscription_response_data *response_data) -{ - struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub); - - if (!exten_state_sub) { - return; + ao2_cleanup(exten_state_sub); + return -1; } - send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_ACTIVE); + send_notify(exten_state_sub); + ao2_cleanup(exten_state_sub); + return 0; } -static void subscription_timeout(struct ast_sip_subscription *sub) +static int notify_required(struct ast_sip_subscription *sub, + enum ast_sip_subscription_notify_reason reason) { - struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub); - - if (!exten_state_sub) { - return; - } + struct exten_state_subscription *exten_state_sub; - ast_verbose(VERBOSE_PREFIX_3 "Subscription has timed out.\n"); - send_notify(exten_state_sub, "timeout", PJSIP_EVSUB_STATE_TERMINATED); -} + switch (reason) { + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED: + return initial_subscribe(sub); + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED: + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED: + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER: + exten_state_sub = get_exten_state_sub(sub); -static void subscription_terminated(struct ast_sip_subscription *sub, - pjsip_rx_data *rdata) -{ - struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub); + if (!exten_state_sub) { + return -1; + } - if (!exten_state_sub) { - return; + send_notify(exten_state_sub); + break; } - ast_verbose(VERBOSE_PREFIX_3 "Subscription has been terminated.\n"); - send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_TERMINATED); + return 0; } static void to_ami(struct ast_sip_subscription *sub, diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c index f25a7c48f..a8e2d1429 100644 --- a/res/res_pjsip_mwi.c +++ b/res/res_pjsip_mwi.c @@ -49,31 +49,24 @@ AO2_GLOBAL_OBJ_STATIC(unsolicited_mwi); #define MWI_SUBTYPE "simple-message-summary" static void mwi_subscription_shutdown(struct ast_sip_subscription *sub); -static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint, - pjsip_rx_data *rdata); -static void mwi_resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, - struct ast_sip_subscription_response_data *response_data); -static void mwi_subscription_timeout(struct ast_sip_subscription *sub); -static void mwi_subscription_terminated(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); -static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); -static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, - struct ast_sip_subscription_response_data *response_data); -static int mwi_refresh_subscription(struct ast_sip_subscription *sub); static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf); +static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint, + const char *resource); +static int mwi_notify_required(struct ast_sip_subscription *sip_sub, + enum ast_sip_subscription_notify_reason reason); + +static struct ast_sip_notifier mwi_notifier = { + .default_accept = MWI_TYPE"/"MWI_SUBTYPE, + .new_subscribe = mwi_new_subscribe, + .notify_required = mwi_notify_required, +}; static struct ast_sip_subscription_handler mwi_handler = { .event_name = "message-summary", .accept = { MWI_TYPE"/"MWI_SUBTYPE, }, - .default_accept = MWI_TYPE"/"MWI_SUBTYPE, .subscription_shutdown = mwi_subscription_shutdown, - .new_subscribe = mwi_new_subscribe, - .resubscribe = mwi_resubscribe, - .subscription_timeout = mwi_subscription_timeout, - .subscription_terminated = mwi_subscription_terminated, - .notify_response = mwi_notify_response, - .notify_request = mwi_notify_request, - .refresh_subscription = mwi_refresh_subscription, .to_ami = mwi_to_ami, + .notifier = &mwi_notifier, }; /*! @@ -202,7 +195,7 @@ static void mwi_subscription_destructor(void *obj) } static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *endpoint, - enum ast_sip_subscription_role role, unsigned int is_solicited, pjsip_rx_data *rdata) + unsigned int is_solicited, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub; const char *endpoint_id = ast_sorcery_object_get_id(endpoint); @@ -216,6 +209,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint * /* Safe strcpy */ strcpy(sub->id, endpoint_id); + /* Unsolicited MWI doesn't actually result in a SIP subscription being * created. This is because a SIP subscription associates with a dialog. * Most devices expect unsolicited MWI NOTIFYs to appear out of dialog. If @@ -224,13 +218,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint * * state not being updated on the device */ if (is_solicited) { - sub->sip_sub = ast_sip_create_subscription(&mwi_handler, - role, endpoint, rdata); - if (!sub->sip_sub) { - ast_log(LOG_WARNING, "Unable to create MWI SIP subscription for endpoint %s\n", sub->id); - ao2_cleanup(sub); - return NULL; - } + sub->sip_sub = ao2_bump(sip_sub); } sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp); @@ -314,7 +302,6 @@ struct unsolicited_mwi_data { struct mwi_subscription *sub; struct ast_sip_endpoint *endpoint; pjsip_evsub_state state; - const char *reason; const struct ast_sip_body *body; }; @@ -324,7 +311,6 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag struct mwi_subscription *sub = mwi_data->sub; struct ast_sip_endpoint *endpoint = mwi_data->endpoint; pjsip_evsub_state state = mwi_data->state; - const char *reason = mwi_data->reason; const struct ast_sip_body *body = mwi_data->body; struct ast_sip_contact *contact = obj; const char *state_name; @@ -358,9 +344,6 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag sub_state = pjsip_sub_state_hdr_create(tdata->pool); pj_cstr(&sub_state->sub_state, state_name); - if (reason) { - pj_cstr(&sub_state->reason_param, reason); - } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) sub_state); event = pjsip_event_hdr_create(tdata->pool); @@ -374,13 +357,15 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag return 0; } -static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state state, const char *reason, - struct ast_sip_body *body) +static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, + struct ast_sip_message_accumulator *counter) { RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", sub->id), ao2_cleanup); char *endpoint_aors; char *aor_name; + struct ast_sip_body body; + struct ast_str *body_text; if (!endpoint) { ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n", @@ -393,17 +378,35 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsu return; } + body.type = MWI_TYPE; + body.subtype = MWI_SUBTYPE; + + body_text = ast_str_create(64); + + if (!body_text) { + return; + } + + if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, counter, &body_text)) { + ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n"); + ast_free(body_text); + return; + } + + body.body_text = ast_str_buffer(body_text); + endpoint_aors = ast_strdupa(endpoint->aors); + ast_debug(5, "Sending unsolicited MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n", + sub->id, counter->new_msgs, counter->old_msgs); + while ((aor_name = strsep(&endpoint_aors, ","))) { RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup); RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); struct unsolicited_mwi_data mwi_data = { .sub = sub, .endpoint = endpoint, - .state = state, - .reason = reason, - .body = body, + .body = &body, }; if (!aor) { @@ -419,63 +422,25 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsu ao2_callback(contacts, OBJ_NODATA, send_unsolicited_mwi_notify_to_contact, &mwi_data); } + + ast_free(body_text); } -static void send_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state state, const char *reason) +static void send_mwi_notify(struct mwi_subscription *sub) { - const pj_str_t *reason_str_ptr = NULL; struct ast_sip_message_accumulator counter = { .old_msgs = 0, .new_msgs = 0, }; - RAII_VAR(struct ast_str *, body_text, ast_str_create(64), ast_free_ptr); - pjsip_tx_data *tdata; - pj_str_t reason_str; - struct ast_sip_body body; - - body.type = sub->is_solicited ? - ast_sip_subscription_get_body_type(sub->sip_sub) : - MWI_TYPE; - - body.subtype = sub->is_solicited ? - ast_sip_subscription_get_body_subtype(sub->sip_sub) : - MWI_SUBTYPE; ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter); - if (reason) { - pj_cstr(&reason_str, reason); - reason_str_ptr = &reason_str; - } - - if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &counter, &body_text)) { - ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n"); + if (sub->is_solicited) { + ast_sip_subscription_notify(sub->sip_sub, &counter, 0); return; } - body.body_text = ast_str_buffer(body_text); - - ast_debug(5, "Sending %s MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n", - sub->is_solicited ? "solicited" : "unsolicited", sub->id, counter.new_msgs, - counter.old_msgs); - - if (sub->is_solicited) { - if (pjsip_evsub_notify(ast_sip_subscription_get_evsub(sub->sip_sub), - state, NULL, reason_str_ptr, &tdata) != PJ_SUCCESS) { - ast_log(LOG_WARNING, "Unable to create MWI NOTIFY request to %s.\n", sub->id); - return; - } - if (ast_sip_add_body(tdata, &body)) { - ast_log(LOG_WARNING, "Unable to add body to MWI NOTIFY request\n"); - return; - } - if (ast_sip_subscription_send_request(sub->sip_sub, tdata) != PJ_SUCCESS) { - ast_log(LOG_WARNING, "Unable to send MWI NOTIFY request to %s\n", sub->id); - return; - } - } else { - send_unsolicited_mwi_notify(sub, state, reason, &body); - } + send_unsolicited_mwi_notify(sub, &counter); } static int unsubscribe_stasis(void *obj, void *arg, int flags) @@ -620,10 +585,9 @@ static int mwi_on_aor(void *obj, void *arg, int flags) } static struct mwi_subscription *mwi_create_subscription( - struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) + struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub) { - struct mwi_subscription *sub = mwi_subscription_alloc( - endpoint, AST_SIP_NOTIFIER, 1, rdata); + struct mwi_subscription *sub = mwi_subscription_alloc(endpoint, 1, sip_sub); if (!sub) { return NULL; @@ -640,29 +604,23 @@ static struct mwi_subscription *mwi_create_subscription( } static struct mwi_subscription *mwi_subscribe_single( - struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *name) + struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name) { RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(name), ao2_cleanup); struct mwi_subscription *sub; if (!aor) { + /*! I suppose it's possible for the AOR to disappear on us + * between accepting the subscription and sending the first + * NOTIFY... + */ ast_log(LOG_WARNING, "Unable to locate aor %s. MWI " "subscription failed.\n", name); return NULL; } - if (ast_strlen_zero(aor->mailboxes)) { - ast_log(LOG_WARNING, "AOR %s has no configured mailboxes. " - "MWI subscription failed\n", name); - return NULL; - } - - if (mwi_validate_for_aor(aor, endpoint, 0)) { - return NULL; - } - - if (!(sub = mwi_create_subscription(endpoint, rdata))) { + if (!(sub = mwi_create_subscription(endpoint, sip_sub))) { return NULL; } @@ -671,15 +629,11 @@ static struct mwi_subscription *mwi_subscribe_single( } static struct mwi_subscription *mwi_subscribe_all( - struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) + struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub; - if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) { - return NULL; - } - - sub = mwi_create_subscription(endpoint, rdata); + sub = mwi_create_subscription(endpoint, sip_sub); if (!sub) { return NULL; @@ -689,106 +643,89 @@ static struct mwi_subscription *mwi_subscribe_all( return sub; } -static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint, - pjsip_rx_data *rdata) +static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint, + const char *resource) { - /* It's not obvious here, but the reference(s) to this subscription, - * once this function exits, is held by the stasis subscription(s) - * created in mwi_stasis_subscription_alloc() - */ - RAII_VAR(struct mwi_subscription *, sub, NULL, ao2_cleanup); - pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri; - pjsip_sip_uri *sip_ruri; - char aor_name[80]; + struct ast_sip_aor *aor; - if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { - ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n"); - return NULL; - } - sip_ruri = pjsip_uri_get_uri(ruri); - ast_copy_pj_str(aor_name, &sip_ruri->user, sizeof(aor_name)); - - /* no aor in uri? subscribe to all on endpoint */ - if (!(sub = ast_strlen_zero(aor_name) ? mwi_subscribe_all(endpoint, rdata) : - mwi_subscribe_single(endpoint, rdata, aor_name))) { - return NULL; + if (ast_strlen_zero(resource)) { + if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) { + return 500; + } + return 200; } - ast_sip_subscription_accept(sub->sip_sub, rdata, 200); - send_mwi_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, NULL); + aor = ast_sip_location_retrieve_aor(resource); - return sub->sip_sub; -} - -static void mwi_resubscribe(struct ast_sip_subscription *sub, - pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data) -{ - struct mwi_subscription *mwi_sub; - pjsip_evsub_state state; - pjsip_evsub *evsub; - RAII_VAR(struct ast_datastore *, mwi_datastore, - ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); + if (!aor) { + ast_log(LOG_WARNING, "Unable to locate aor %s. MWI " + "subscription failed.\n", resource); + return 404; + } - if (!mwi_datastore) { - return; + if (ast_strlen_zero(aor->mailboxes)) { + ast_log(LOG_WARNING, "AOR %s has no configured mailboxes. " + "MWI subscription failed\n", resource); + return 404; } - mwi_sub = mwi_datastore->data; - evsub = ast_sip_subscription_get_evsub(sub); - state = pjsip_evsub_get_state(evsub); + if (mwi_validate_for_aor(aor, endpoint, 0)) { + return 500; + } - send_mwi_notify(mwi_sub, state, NULL); + return 200; } -static void mwi_subscription_timeout(struct ast_sip_subscription *sub) +static int mwi_initial_subscription(struct ast_sip_subscription *sip_sub) { - struct mwi_subscription *mwi_sub; - RAII_VAR(struct ast_datastore *, mwi_datastore, - ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); + const char *resource = ast_sip_subscription_get_resource_name(sip_sub); + struct mwi_subscription *sub; + struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub); - if (!mwi_datastore) { - return; + /* no aor in uri? subscribe to all on endpoint */ + if (ast_strlen_zero(resource)) { + sub = mwi_subscribe_all(endpoint, sip_sub); + } else { + sub = mwi_subscribe_single(endpoint, sip_sub, resource); } + if (!sub) { + ao2_cleanup(endpoint); + return -1; + } - mwi_sub = mwi_datastore->data; - - ast_log(LOG_NOTICE, "MWI subscription for %s has timed out.\n", mwi_sub->id); + send_mwi_notify(sub); - send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_TERMINATED, "timeout"); + ao2_cleanup(sub); + ao2_cleanup(endpoint); + return 0; } -static void mwi_subscription_terminated(struct ast_sip_subscription *sub, pjsip_rx_data *rdata) +static int mwi_notify_required(struct ast_sip_subscription *sip_sub, + enum ast_sip_subscription_notify_reason reason) { struct mwi_subscription *mwi_sub; - RAII_VAR(struct ast_datastore *, mwi_datastore, - ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); - - if (!mwi_datastore) { - return; - } + struct ast_datastore *mwi_datastore; - mwi_sub = mwi_datastore->data; + switch (reason) { + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED: + return mwi_initial_subscription(sip_sub); + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED: + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED: + case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER: + mwi_datastore = ast_sip_subscription_get_datastore(sip_sub, "MWI datastore"); - ast_log(LOG_NOTICE, "MWI subscription for %s has been terminated\n", mwi_sub->id); + if (!mwi_datastore) { + return -1; + } - send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_TERMINATED, NULL); -} + mwi_sub = mwi_datastore->data; -static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata) -{ - /* We don't really care about NOTIFY responses for the moment */ -} - -static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, - struct ast_sip_subscription_response_data *response_data) -{ - ast_log(LOG_WARNING, "Received an MWI NOTIFY request? This should not happen\n"); -} + send_mwi_notify(mwi_sub); + ao2_cleanup(mwi_datastore); + break; + } -static int mwi_refresh_subscription(struct ast_sip_subscription *sub) -{ - ast_log(LOG_WARNING, "Being told to refresh an MWI subscription? This should not happen\n"); return 0; } @@ -834,7 +771,7 @@ static int serialized_notify(void *userdata) { struct mwi_subscription *mwi_sub = userdata; - send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_ACTIVE, NULL); + send_mwi_notify(mwi_sub); ao2_ref(mwi_sub, -1); return 0; } @@ -885,7 +822,7 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags } if (endpoint->subscription.mwi.aggregate) { - aggregate_sub = mwi_subscription_alloc(endpoint, AST_SIP_NOTIFIER, 0, NULL); + aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL); if (!aggregate_sub) { return 0; } @@ -894,7 +831,7 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags mailboxes = ast_strdupa(endpoint->subscription.mwi.mailboxes); while ((mailbox = strsep(&mailboxes, ","))) { struct mwi_subscription *sub = aggregate_sub ?: - mwi_subscription_alloc(endpoint, AST_SIP_SUBSCRIBER, 0, NULL); + mwi_subscription_alloc(endpoint, 0, NULL); RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub, mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup); if (mwi_stasis_sub) { diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c index 875a65fda..690051e13 100644 --- a/res/res_pjsip_pidf_body_generator.c +++ b/res/res_pjsip_pidf_body_generator.c @@ -79,7 +79,7 @@ static int pidf_generate_body_content(void *body, void *data) return 0; } -#define MAX_STRING_GROWTHS 3 +#define MAX_STRING_GROWTHS 5 #define XML_PROLOG 39 static void pidf_to_string(void *body, struct ast_str **str) diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c index dfca643bc..39846823e 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -120,8 +120,8 @@ static struct pjsip_module pubsub_module = { .on_rx_request = pubsub_on_rx_request, }; -#define MOD_DATA_BODY_GENERATOR "sub_body_generator" #define MOD_DATA_PERSISTENCE "sub_persistence" +#define MOD_DATA_MSG "sub_msg" static const pj_str_t str_event_name = { "Event", 5 }; @@ -249,6 +249,55 @@ struct subscription_persistence { }; /*! + * \brief Real subscription details + * + * A real subscription is one that has a direct link to a + * PJSIP subscription and dialog. + */ +struct ast_sip_real_subscription { + /*! The underlying PJSIP event subscription structure */ + pjsip_evsub *evsub; + /*! The underlying PJSIP dialog */ + pjsip_dialog *dlg; +}; + +/*! + * \brief Virtual subscription details + * + * A virtual subscription is one that does not have a direct + * link to a PJSIP subscription. Instead, it is a descendent + * of an ast_sip_subscription. Following the ancestry will + * eventually lead to a real subscription. + */ +struct ast_sip_virtual_subscription { + struct ast_sip_subscription *parent; +}; + +/*! + * \brief Discriminator between real and virtual subscriptions + */ +enum sip_subscription_type { + /*! + * \brief a "real" subscription. + * + * Real subscriptions are at the root of a tree of subscriptions. + * A real subscription has a corresponding SIP subscription in the + * PJSIP stack. + */ + SIP_SUBSCRIPTION_REAL, + /*! + * \brief a "virtual" subscription. + * + * Virtual subscriptions are the descendents of real subscriptions + * in a tree of subscriptions. Virtual subscriptions do not have + * a corresponding SIP subscription in the PJSIP stack. Instead, + * when a state change happens on a virtual subscription, the + * state change is indicated to the virtual subscription's parent. + */ + SIP_SUBSCRIPTION_VIRTUAL, +}; + +/*! * \brief Structure representing a SIP subscription */ struct ast_sip_subscription { @@ -262,16 +311,23 @@ struct ast_sip_subscription { const struct ast_sip_subscription_handler *handler; /*! The role for this subscription */ enum ast_sip_subscription_role role; - /*! The underlying PJSIP event subscription structure */ - pjsip_evsub *evsub; - /*! The underlying PJSIP dialog */ - pjsip_dialog *dlg; + /*! Indicator of real or virtual subscription */ + enum sip_subscription_type type; + /*! Real and virtual components of the subscription */ + union { + struct ast_sip_real_subscription real; + struct ast_sip_virtual_subscription virtual; + } reality; /*! Body generaator for NOTIFYs */ struct ast_sip_pubsub_body_generator *body_generator; /*! Persistence information */ struct subscription_persistence *persistence; /*! Next item in the list */ AST_LIST_ENTRY(ast_sip_subscription) next; + /*! List of child subscriptions */ + AST_LIST_HEAD_NOLOCK(,ast_sip_subscription) children; + /*! Name of resource being subscribed to */ + char resource[0]; }; static const char *sip_subscription_roles_map[] = { @@ -284,6 +340,16 @@ AST_RWLIST_HEAD_STATIC(subscriptions, ast_sip_subscription); AST_RWLIST_HEAD_STATIC(body_generators, ast_sip_pubsub_body_generator); AST_RWLIST_HEAD_STATIC(body_supplements, ast_sip_pubsub_body_supplement); +static pjsip_evsub *sip_subscription_get_evsub(const struct ast_sip_subscription *sub) +{ + return sub->reality.real.evsub; +} + +static pjsip_dialog *sip_subscription_get_dlg(const struct ast_sip_subscription *sub) +{ + return sub->reality.real.dlg; +} + /*! \brief Destructor for subscription persistence */ static void subscription_persistence_destroy(void *obj) { @@ -310,12 +376,14 @@ static struct subscription_persistence *subscription_persistence_create(struct a struct subscription_persistence *persistence = ast_sorcery_alloc(ast_sip_get_sorcery(), "subscription_persistence", NULL); + pjsip_dialog *dlg = sip_subscription_get_dlg(sub); + if (!persistence) { return NULL; } persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub->endpoint)); - ast_copy_pj_str(tag, &sub->dlg->local.info->tag, sizeof(tag)); + ast_copy_pj_str(tag, &dlg->local.info->tag, sizeof(tag)); persistence->tag = ast_strdup(tag); ast_sorcery_create(ast_sip_get_sorcery(), persistence); @@ -326,11 +394,14 @@ static struct subscription_persistence *subscription_persistence_create(struct a static void subscription_persistence_update(struct ast_sip_subscription *sub, pjsip_rx_data *rdata) { + pjsip_dialog *dlg; + if (!sub->persistence) { return; } - sub->persistence->cseq = sub->dlg->local.cseq; + dlg = sip_subscription_get_dlg(sub); + sub->persistence->cseq = dlg->local.cseq; if (rdata) { int expires; @@ -410,13 +481,17 @@ static struct ast_sip_pubsub_body_generator *subscription_get_generator_from_rda /* If a SUBSCRIBE contains no Accept headers, then we must assume that * the default accept type for the event package is to be used. */ - ast_copy_string(accept[0], handler->default_accept, sizeof(accept[0])); + ast_copy_string(accept[0], handler->notifier->default_accept, sizeof(accept[0])); num_accept_headers = 1; } return find_body_generator(accept, num_accept_headers); } +static struct ast_sip_subscription *notifier_create_subscription(const struct ast_sip_subscription_handler *handler, + struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource, + struct ast_sip_pubsub_body_generator *generator); + /*! \brief Callback function to perform the actual recreation of a subscription */ static int subscription_persistence_recreate(void *obj, void *arg, int flags) { @@ -428,6 +503,10 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags) RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); struct ast_sip_subscription *sub; struct ast_sip_pubsub_body_generator *generator; + int resp; + char *resource; + size_t resource_size; + pjsip_sip_uri *request_uri; /* If this subscription has already expired remove it */ if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) { @@ -454,6 +533,11 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags) return 0; } + request_uri = pjsip_uri_get_uri(rdata.msg_info.msg->line.req.uri); + resource_size = pj_strlen(&request_uri->user) + 1; + resource = alloca(resource_size); + ast_copy_pj_str(resource, &request_uri->user, resource_size); + /* Update the expiration header with the new expiration */ expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, rdata.msg_info.msg->hdr.next); if (!expires_header) { @@ -467,7 +551,7 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags) expires_header->ivalue = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000); handler = subscription_get_handler_from_rdata(&rdata); - if (!handler) { + if (!handler || !handler->notifier) { ast_sorcery_delete(ast_sip_get_sorcery(), persistence); return 0; } @@ -479,12 +563,11 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags) } ast_sip_mod_data_set(rdata.tp_info.pool, rdata.endpt_info.mod_data, - pubsub_module.id, MOD_DATA_BODY_GENERATOR, generator); - ast_sip_mod_data_set(rdata.tp_info.pool, rdata.endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE, persistence); - sub = handler->new_subscribe(endpoint, &rdata); - if (sub) { + resp = handler->notifier->new_subscribe(endpoint, resource); + if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) { + sub = notifier_create_subscription(handler, endpoint, &rdata, resource, generator); sub->persistence = ao2_bump(persistence); subscription_persistence_update(sub, &rdata); } else { @@ -596,11 +679,11 @@ static void sip_subscription_to_ami(struct ast_sip_subscription *sub, ast_str_append(buf, 0, "Endpoint: %s\r\n", ast_sorcery_object_get_id(sub->endpoint)); - ast_copy_pj_str(str, &sub->dlg->call_id->id, sizeof(str)); + ast_copy_pj_str(str, &sip_subscription_get_dlg(sub)->call_id->id, sizeof(str)); ast_str_append(buf, 0, "Callid: %s\r\n", str); ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name( - ast_sip_subscription_get_evsub(sub))); + sip_subscription_get_evsub(sub))); ast_callerid_merge(str, sizeof(str), S_COR(id->self.name.valid, id->self.name.str, NULL), @@ -653,8 +736,8 @@ static int subscription_remove_serializer(void *obj) * subscription is destroyed so that we can guarantee that our attempt to * remove the serializer will be successful. */ - ast_sip_dialog_set_serializer(sub->dlg, NULL); - pjsip_dlg_dec_session(sub->dlg, &pubsub_module); + ast_sip_dialog_set_serializer(sip_subscription_get_dlg(sub), NULL); + pjsip_dlg_dec_session(sip_subscription_get_dlg(sub), &pubsub_module); return 0; } @@ -672,14 +755,14 @@ static void subscription_destructor(void *obj) ao2_cleanup(sub->datastores); ao2_cleanup(sub->endpoint); - if (sub->dlg) { + if (sip_subscription_get_dlg(sub)) { ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub); } ast_taskprocessor_unreference(sub->serializer); } + static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event); -static void pubsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, @@ -690,41 +773,23 @@ static void pubsub_on_server_timeout(pjsip_evsub *sub); static pjsip_evsub_user pubsub_cb = { .on_evsub_state = pubsub_on_evsub_state, - .on_tsx_state = pubsub_on_tsx_state, .on_rx_refresh = pubsub_on_rx_refresh, .on_rx_notify = pubsub_on_rx_notify, .on_client_refresh = pubsub_on_client_refresh, .on_server_timeout = pubsub_on_server_timeout, }; -static pjsip_evsub *allocate_evsub(const char *event, enum ast_sip_subscription_role role, - struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_dialog *dlg) -{ - pjsip_evsub *evsub; - /* PJSIP is kind enough to have some built-in support for certain - * events. We need to use the correct initialization function for the - * built-in events - */ - if (role == AST_SIP_NOTIFIER) { - pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &evsub); - } else { - pj_str_t pj_event; - pj_cstr(&pj_event, event); - pjsip_evsub_create_uac(dlg, &pubsub_cb, &pj_event, 0, &evsub); - } - return evsub; -} - -struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler, - enum ast_sip_subscription_role role, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) +static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler, + struct ast_sip_endpoint *endpoint, const char *resource, enum ast_sip_subscription_role role) { - struct ast_sip_subscription *sub = ao2_alloc(sizeof(*sub), subscription_destructor); - pjsip_dialog *dlg; - struct subscription_persistence *persistence; + struct ast_sip_subscription *sub; + sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor); if (!sub) { return NULL; } + strcpy(sub->resource, resource); /* Safe */ + sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp); if (!sub->datastores) { ao2_ref(sub, -1); @@ -735,28 +800,46 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su ao2_ref(sub, -1); return NULL; } - sub->body_generator = ast_sip_mod_data_get(rdata->endpt_info.mod_data, - pubsub_module.id, MOD_DATA_BODY_GENERATOR); sub->role = role; - if (role == AST_SIP_NOTIFIER) { - dlg = ast_sip_create_dialog_uas(endpoint, rdata); - } else { - RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + sub->type = SIP_SUBSCRIPTION_REAL; + sub->endpoint = ao2_bump(endpoint); + sub->handler = handler; - contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors); - if (!contact || ast_strlen_zero(contact->uri)) { - ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n", - ast_sorcery_object_get_id(endpoint)); - ao2_ref(sub, -1); - return NULL; - } - dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL); + return sub; +} + +static void subscription_setup_dialog(struct ast_sip_subscription *sub, pjsip_dialog *dlg) +{ + /* We keep a reference to the dialog until our subscription is destroyed. See + * the subscription_destructor for more details + */ + pjsip_dlg_inc_session(dlg, &pubsub_module); + sub->reality.real.dlg = dlg; + ast_sip_dialog_set_serializer(dlg, sub->serializer); + pjsip_evsub_set_mod_data(sip_subscription_get_evsub(sub), pubsub_module.id, sub); +} + +static struct ast_sip_subscription *notifier_create_subscription(const struct ast_sip_subscription_handler *handler, + struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource, + struct ast_sip_pubsub_body_generator *generator) +{ + struct ast_sip_subscription *sub; + pjsip_dialog *dlg; + struct subscription_persistence *persistence; + + sub = allocate_subscription(handler, endpoint, resource, AST_SIP_NOTIFIER); + if (!sub) { + return NULL; } + + sub->body_generator = generator; + dlg = ast_sip_create_dialog_uas(endpoint, rdata); if (!dlg) { ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n"); ao2_ref(sub, -1); return NULL; } + persistence = ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE); if (persistence) { @@ -768,62 +851,102 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su dlg->local.cseq = persistence->cseq; dlg->remote.cseq = persistence->cseq; } - sub->evsub = allocate_evsub(handler->event_name, role, endpoint, rdata, dlg); - /* We keep a reference to the dialog until our subscription is destroyed. See - * the subscription_destructor for more details - */ - pjsip_dlg_inc_session(dlg, &pubsub_module); - sub->dlg = dlg; - ast_sip_dialog_set_serializer(dlg, sub->serializer); - pjsip_evsub_set_mod_data(sub->evsub, pubsub_module.id, sub); - ao2_ref(endpoint, +1); - sub->endpoint = endpoint; - sub->handler = handler; + + pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub->reality.real.evsub); + subscription_setup_dialog(sub, dlg); + + ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG, + pjsip_msg_clone(dlg->pool, rdata->msg_info.msg)); add_subscription(sub); return sub; } -struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub) +void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header) { - ast_assert(sub->endpoint != NULL); - ao2_ref(sub->endpoint, +1); - return sub->endpoint; -} + pjsip_dialog *dlg = sip_subscription_get_dlg(sub); + pjsip_msg *msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG); + pj_str_t name; -struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub) -{ - ast_assert(sub->serializer != NULL); - return sub->serializer; + pj_cstr(&name, header); + + return pjsip_msg_find_hdr_by_name(msg, &name, NULL); } -pjsip_evsub *ast_sip_subscription_get_evsub(struct ast_sip_subscription *sub) +struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler, + struct ast_sip_endpoint *endpoint, const char *resource) { - return sub->evsub; + struct ast_sip_subscription *sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor); + pjsip_dialog *dlg; + struct ast_sip_contact *contact; + pj_str_t event; + pjsip_tx_data *tdata; + pjsip_evsub *evsub; + + sub = allocate_subscription(handler, endpoint, resource, AST_SIP_SUBSCRIBER); + if (!sub) { + return NULL; + } + + contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors); + if (!contact || ast_strlen_zero(contact->uri)) { + ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n", + ast_sorcery_object_get_id(endpoint)); + ao2_ref(sub, -1); + ao2_cleanup(contact); + return NULL; + } + + dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL); + ao2_cleanup(contact); + if (!dlg) { + ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n"); + ao2_ref(sub, -1); + return NULL; + } + + pj_cstr(&event, handler->event_name); + pjsip_evsub_create_uac(dlg, &pubsub_cb, &event, 0, &sub->reality.real.evsub); + subscription_setup_dialog(sub, dlg); + + add_subscription(sub); + + evsub = sip_subscription_get_evsub(sub); + + if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) { + pjsip_evsub_send_request(evsub, tdata); + } else { + /* pjsip_evsub_terminate will result in pubsub_on_evsub_state, + * being called and terminating the subscription. Therefore, we don't + * need to decrease the reference count of sub here. + */ + pjsip_evsub_terminate(evsub, PJ_TRUE); + return NULL; + } + + return sub; } -pjsip_dialog *ast_sip_subscription_get_dlg(struct ast_sip_subscription *sub) +struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub) { - return sub->dlg; + ast_assert(sub->endpoint != NULL); + ao2_ref(sub->endpoint, +1); + return sub->endpoint; } -int ast_sip_subscription_accept(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, int response) +struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub) { - /* If this is a persistence recreation the subscription has already been accepted */ - if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) { - return 0; - } - - return pjsip_evsub_accept(ast_sip_subscription_get_evsub(sub), rdata, response, NULL) == PJ_SUCCESS ? 0 : -1; + ast_assert(sub->serializer != NULL); + return sub->serializer; } -int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx_data *tdata) +static int sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx_data *tdata) { struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sub); int res; ao2_ref(sub, +1); - res = pjsip_evsub_send_request(ast_sip_subscription_get_evsub(sub), + res = pjsip_evsub_send_request(sip_subscription_get_evsub(sub), tdata) == PJ_SUCCESS ? 0 : -1; subscription_persistence_update(sub, NULL); @@ -831,7 +954,7 @@ int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET", "StateText: %s\r\n" "Endpoint: %s\r\n", - pjsip_evsub_get_state_name(ast_sip_subscription_get_evsub(sub)), + pjsip_evsub_get_state_name(sip_subscription_get_evsub(sub)), ast_sorcery_object_get_id(endpoint)); ao2_cleanup(sub); ao2_cleanup(endpoint); @@ -839,6 +962,83 @@ int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx return res; } +int ast_sip_subscription_notify(struct ast_sip_subscription *sub, void *notify_data, + int terminate) +{ + struct ast_sip_body body = { + .type = ast_sip_subscription_get_body_type(sub), + .subtype = ast_sip_subscription_get_body_subtype(sub), + }; + struct ast_str *body_text = ast_str_create(64); + pjsip_evsub *evsub = sip_subscription_get_evsub(sub); + pjsip_tx_data *tdata; + pjsip_evsub_state state; + + if (!body_text) { + return -1; + } + + if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, notify_data, &body_text)) { + ast_free(body_text); + return -1; + } + + body.body_text = ast_str_buffer(body_text); + + if (terminate) { + state = PJSIP_EVSUB_STATE_TERMINATED; + } else { + state = pjsip_evsub_get_state(evsub) <= PJSIP_EVSUB_STATE_ACTIVE ? + PJSIP_EVSUB_STATE_ACTIVE : PJSIP_EVSUB_STATE_TERMINATED; + } + + ast_log_backtrace(); + + if (pjsip_evsub_notify(evsub, state, NULL, NULL, &tdata) != PJ_SUCCESS) { + ast_free(body_text); + return -1; + } + if (ast_sip_add_body(tdata, &body)) { + ast_free(body_text); + pjsip_tx_data_dec_ref(tdata); + return -1; + } + if (sip_subscription_send_request(sub, tdata)) { + ast_free(body_text); + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + return 0; +} + +void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size) +{ + pjsip_dialog *dlg = sip_subscription_get_dlg(sub); + ast_copy_pj_str(buf, &dlg->local.info_str, size); +} + +void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size) +{ + pjsip_dialog *dlg = sip_subscription_get_dlg(sub); + ast_copy_pj_str(buf, &dlg->remote.info_str, size); +} + +const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub) +{ + return sub->resource; +} + +static int sip_subscription_accept(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, int response) +{ + /* If this is a persistence recreation the subscription has already been accepted */ + if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) { + return 0; + } + + return pjsip_evsub_accept(sip_subscription_get_evsub(sub), rdata, response, NULL) == PJ_SUCCESS ? 0 : -1; +} + static void subscription_datastore_destroy(void *obj) { struct ast_datastore *datastore = obj; @@ -1019,9 +1219,9 @@ static struct ast_sip_subscription_handler *find_sub_handler_for_event_name(cons int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler) { pj_str_t event; - pj_str_t accept[AST_SIP_MAX_ACCEPT]; + pj_str_t accept[AST_SIP_MAX_ACCEPT] = { {0, }, }; struct ast_sip_subscription_handler *existing; - int i; + int i = 0; if (ast_strlen_zero(handler->event_name)) { ast_log(LOG_ERROR, "No event package specified for subscription handler. Cannot register\n"); @@ -1117,6 +1317,11 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata) RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); struct ast_sip_subscription *sub; struct ast_sip_pubsub_body_generator *generator; + char *resource; + pjsip_uri *request_uri; + pjsip_sip_uri *request_uri_sip; + size_t resource_size; + int resp; endpoint = ast_pjsip_rdata_get_endpoint(rdata); ast_assert(endpoint != NULL); @@ -1127,6 +1332,22 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata) return PJ_TRUE; } + request_uri = rdata->msg_info.msg->line.req.uri; + + if (!PJSIP_URI_SCHEME_IS_SIP(request_uri) && !PJSIP_URI_SCHEME_IS_SIPS(request_uri)) { + char uri_str[PJSIP_MAX_URL_SIZE]; + + pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str)); + ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); + return PJ_TRUE; + } + + request_uri_sip = pjsip_uri_get_uri(request_uri); + resource_size = pj_strlen(&request_uri_sip->user) + 1; + resource = alloca(resource_size); + ast_copy_pj_str(resource, &request_uri_sip->user, resource_size); + expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next); if (expires_header) { @@ -1142,7 +1363,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata) pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 423, NULL, NULL, NULL); return PJ_TRUE; } - } + } handler = subscription_get_handler_from_rdata(rdata); if (!handler) { @@ -1156,27 +1377,22 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata) return PJ_TRUE; } - ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data, - pubsub_module.id, MOD_DATA_BODY_GENERATOR, generator); + resp = handler->notifier->new_subscribe(endpoint, resource); + if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL); + return PJ_TRUE; + } - sub = handler->new_subscribe(endpoint, rdata); + sub = notifier_create_subscription(handler, endpoint, rdata, resource, generator); if (!sub) { - pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata); - - if (trans) { - pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); - pjsip_tx_data *tdata; - - if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, &tdata) != PJ_SUCCESS) { - return PJ_TRUE; - } - pjsip_dlg_send_response(dlg, trans, tdata); - } else { - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); - } + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); } else { sub->persistence = subscription_persistence_create(sub); subscription_persistence_update(sub, rdata); + sip_subscription_accept(sub, rdata, resp); + if (handler->notifier->notify_required(sub, AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED)) { + pjsip_evsub_terminate(sip_subscription_get_evsub(sub), PJ_TRUE); + } } return PJ_TRUE; @@ -1229,10 +1445,114 @@ static enum sip_publish_type determine_sip_publish_type(pjsip_rx_data *rdata, return SIP_PUBLISH_UNKNOWN; } +/*! \brief Internal destructor for publications */ +static void publication_destroy_fn(void *obj) +{ + struct ast_sip_publication *publication = obj; + + ast_debug(3, "Destroying SIP publication\n"); + + ao2_cleanup(publication->datastores); + ao2_cleanup(publication->endpoint); +} + +static struct ast_sip_publication *sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) +{ + struct ast_sip_publication *publication; + pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); + + ast_assert(endpoint != NULL); + + if (!(publication = ao2_alloc(sizeof(*publication), publication_destroy_fn))) { + return NULL; + } + + if (!(publication->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp))) { + ao2_ref(publication, -1); + return NULL; + } + + publication->entity_tag = ast_atomic_fetchadd_int(&esc_etag_counter, +1); + ao2_ref(endpoint, +1); + publication->endpoint = endpoint; + publication->expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES; + publication->sched_id = -1; + + return publication; +} + +static int sip_publication_respond(struct ast_sip_publication *pub, int status_code, + pjsip_rx_data *rdata) +{ + pj_status_t status; + pjsip_tx_data *tdata; + pjsip_transaction *tsx; + + if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, &tdata) != PJ_SUCCESS) { + return -1; + } + + if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) { + RAII_VAR(char *, entity_tag, NULL, ast_free_ptr); + RAII_VAR(char *, expires, NULL, ast_free_ptr); + + if ((ast_asprintf(&entity_tag, "%d", pub->entity_tag) < 0) || + (ast_asprintf(&expires, "%d", pub->expires) < 0)) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + ast_sip_add_header(tdata, "SIP-ETag", entity_tag); + ast_sip_add_header(tdata, "Expires", expires); + } + + if ((status = pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx)) != PJ_SUCCESS) { + return -1; + } + + pjsip_tsx_recv_msg(tsx, rdata); + + if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) { + return -1; + } + + return 0; +} + static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, struct ast_sip_publish_handler *handler) { - struct ast_sip_publication *publication = handler->new_publication(endpoint, rdata); + struct ast_sip_publication *publication; + char *resource; + size_t resource_size; + pjsip_uri *request_uri; + pjsip_sip_uri *request_uri_sip; + int resp; + + request_uri = rdata->msg_info.msg->line.req.uri; + + if (!PJSIP_URI_SCHEME_IS_SIP(request_uri) && !PJSIP_URI_SCHEME_IS_SIPS(request_uri)) { + char uri_str[PJSIP_MAX_URL_SIZE]; + + pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str)); + ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); + return NULL; + } + + request_uri_sip = pjsip_uri_get_uri(request_uri); + resource_size = pj_strlen(&request_uri_sip->user) + 1; + resource = alloca(resource_size); + ast_copy_pj_str(resource, &request_uri_sip->user, resource_size); + + resp = handler->new_publication(endpoint, resource); + + if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL); + return NULL; + } + + publication = sip_create_publication(endpoint, rdata); if (!publication) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL); @@ -1240,6 +1560,14 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi } publication->handler = handler; + if (publication->handler->publication_state_change(publication, rdata->msg_info.msg->body, + AST_SIP_PUBLISH_STATE_INITIALIZED)) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); + ao2_cleanup(publication); + return NULL; + } + + sip_publication_respond(publication, resp, rdata); return publication; } @@ -1321,14 +1649,19 @@ static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata) publication = publish_request_initial(endpoint, rdata, handler); break; case SIP_PUBLISH_REFRESH: + sip_publication_respond(publication, 200, rdata); case SIP_PUBLISH_MODIFY: - if (handler->publish_refresh(publication, rdata)) { + if (handler->publication_state_change(publication, rdata->msg_info.msg->body, + AST_SIP_PUBLISH_STATE_ACTIVE)) { /* If an error occurs we want to terminate the publication */ expires = 0; } + sip_publication_respond(publication, 200, rdata); break; case SIP_PUBLISH_REMOVE: - handler->publish_termination(publication, rdata); + handler->publication_state_change(publication, rdata->msg_info.msg->body, + AST_SIP_PUBLISH_STATE_TERMINATED); + sip_publication_respond(publication, 200, rdata); break; case SIP_PUBLISH_UNKNOWN: default: @@ -1350,85 +1683,11 @@ static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata) return PJ_TRUE; } -/*! \brief Internal destructor for publications */ -static void publication_destroy_fn(void *obj) -{ - struct ast_sip_publication *publication = obj; - - ast_debug(3, "Destroying SIP publication\n"); - - ao2_cleanup(publication->datastores); - ao2_cleanup(publication->endpoint); -} - -struct ast_sip_publication *ast_sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) -{ - struct ast_sip_publication *publication; - pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); - - ast_assert(endpoint != NULL); - - if (!(publication = ao2_alloc(sizeof(*publication), publication_destroy_fn))) { - return NULL; - } - - if (!(publication->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp))) { - ao2_ref(publication, -1); - return NULL; - } - - publication->entity_tag = ast_atomic_fetchadd_int(&esc_etag_counter, +1); - ao2_ref(endpoint, +1); - publication->endpoint = endpoint; - publication->expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES; - publication->sched_id = -1; - - return publication; -} - struct ast_sip_endpoint *ast_sip_publication_get_endpoint(struct ast_sip_publication *pub) { return pub->endpoint; } -int ast_sip_publication_create_response(struct ast_sip_publication *pub, int status_code, pjsip_rx_data *rdata, - pjsip_tx_data **tdata) -{ - if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, tdata) != PJ_SUCCESS) { - return -1; - } - - if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) { - RAII_VAR(char *, entity_tag, NULL, ast_free_ptr); - RAII_VAR(char *, expires, NULL, ast_free_ptr); - - if ((ast_asprintf(&entity_tag, "%d", pub->entity_tag) < 0) || - (ast_asprintf(&expires, "%d", pub->expires) < 0)) { - pjsip_tx_data_dec_ref(*tdata); - return -1; - } - - ast_sip_add_header(*tdata, "SIP-ETag", entity_tag); - ast_sip_add_header(*tdata, "Expires", expires); - } - - return 0; -} - -pj_status_t ast_sip_publication_send_response(struct ast_sip_publication *pub, pjsip_rx_data *rdata, - pjsip_tx_data *tdata) -{ - pj_status_t status; - pjsip_transaction *tsx; - - if ((status = pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx)) != PJ_SUCCESS) { - return status; - } - - pjsip_tsx_recv_msg(tsx, rdata); - - return pjsip_tsx_send_msg(tsx, tdata); -} int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator) { @@ -1590,123 +1849,53 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event) pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL); } -static void pubsub_on_tsx_state(pjsip_evsub *evsub, pjsip_transaction *tsx, pjsip_event *event) -{ - struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id); - - if (!sub) { - return; - } - - if (sub->handler->notify_response && tsx->role == PJSIP_ROLE_UAC && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { - sub->handler->notify_response(sub, event->body.tsx_state.src.rdata); - } -} - -static void set_parameters_from_response_data(pj_pool_t *pool, int *p_st_code, - pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body, - struct ast_sip_subscription_response_data *response_data) -{ - ast_assert(response_data->status_code >= 200 && response_data->status_code <= 699); - *p_st_code = response_data->status_code; - - if (!ast_strlen_zero(response_data->status_text)) { - pj_strdup2(pool, *p_st_text, response_data->status_text); - } - - if (response_data->headers) { - struct ast_variable *iter; - for (iter = response_data->headers; iter; iter = iter->next) { - pj_str_t header_name; - pj_str_t header_value; - pjsip_generic_string_hdr *hdr; - - pj_cstr(&header_name, iter->name); - pj_cstr(&header_value, iter->value); - hdr = pjsip_generic_string_hdr_create(pool, &header_name, &header_value); - pj_list_insert_before(res_hdr, hdr); - } - } - - if (response_data->body) { - pj_str_t type; - pj_str_t subtype; - pj_str_t body_text; - - pj_cstr(&type, response_data->body->type); - pj_cstr(&subtype, response_data->body->subtype); - pj_cstr(&body_text, response_data->body->body_text); - - *p_body = pjsip_msg_body_create(pool, &type, &subtype, &body_text); - } -} - -static int response_data_changed(struct ast_sip_subscription_response_data *response_data) -{ - if (response_data->status_code != 200 || - !ast_strlen_zero(response_data->status_text) || - response_data->headers || - response_data->body) { - return 1; - } - return 0; -} - static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id); - struct ast_sip_subscription_response_data response_data = { - .status_code = 200, - }; + enum ast_sip_subscription_notify_reason reason; if (!sub) { return; } - if (pjsip_evsub_get_state(sub->evsub) == PJSIP_EVSUB_STATE_TERMINATED) { - sub->handler->subscription_terminated(sub, rdata); - return; + if (pjsip_evsub_get_state(sip_subscription_get_evsub(sub)) == PJSIP_EVSUB_STATE_TERMINATED) { + reason = AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED; + } else { + reason = AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED; } - - sub->handler->resubscribe(sub, rdata, &response_data); - - if (!response_data_changed(&response_data)) { - return; + if (sub->handler->notifier->notify_required(sub, reason)) { + *p_st_code = 500; } - - set_parameters_from_response_data(rdata->tp_info.pool, p_st_code, p_st_text, - res_hdr, p_body, &response_data); } static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id); - struct ast_sip_subscription_response_data response_data = { - .status_code = 200, - }; - - if (!sub || !sub->handler->notify_request) { - return; - } - - sub->handler->notify_request(sub, rdata, &response_data); - if (!response_data_changed(&response_data)) { + if (!sub) { return; } - set_parameters_from_response_data(rdata->tp_info.pool, p_st_code, p_st_text, - res_hdr, p_body, &response_data); + sub->handler->subscriber->state_change(sub, rdata->msg_info.msg->body, + pjsip_evsub_get_state(evsub)); } static int serialized_pubsub_on_client_refresh(void *userdata) { struct ast_sip_subscription *sub = userdata; + pjsip_evsub *evsub; + pjsip_tx_data *tdata; + + evsub = sip_subscription_get_evsub(sub); - sub->handler->refresh_subscription(sub); + if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) { + pjsip_evsub_send_request(evsub, tdata); + } else { + pjsip_evsub_terminate(evsub, PJ_TRUE); + return 0; + } ao2_cleanup(sub); return 0; } @@ -1723,7 +1912,9 @@ static int serialized_pubsub_on_server_timeout(void *userdata) { struct ast_sip_subscription *sub = userdata; - sub->handler->subscription_timeout(sub); + sub->handler->notifier->notify_required(sub, + AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED); + ao2_cleanup(sub); return 0; } diff --git a/res/res_pjsip_pubsub.exports.in b/res/res_pjsip_pubsub.exports.in index 0877d5c6c..ca165af92 100644 --- a/res/res_pjsip_pubsub.exports.in +++ b/res/res_pjsip_pubsub.exports.in @@ -1,7 +1,7 @@ { global: LINKER_SYMBOL_PREFIXast_sip_create_subscription; - LINKER_SYMBOL_PREFIXast_sip_subsription_get_endpoint; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_endpoint; LINKER_SYMBOL_PREFIXast_sip_subscription_get_serializer; LINKER_SYMBOL_PREFIXast_sip_subscription_get_evsub; LINKER_SYMBOL_PREFIXast_sip_subscription_get_dlg; @@ -30,6 +30,11 @@ LINKER_SYMBOL_PREFIXast_sip_pubsub_generate_body_content; LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_type; LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_subtype; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_resource_name; + LINKER_SYMBOL_PREFIXast_sip_subscription_notify; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_local_uri; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_header; local: *; }; diff --git a/res/res_pjsip_xpidf_body_generator.c b/res/res_pjsip_xpidf_body_generator.c index fba6152b3..aeb313f12 100644 --- a/res/res_pjsip_xpidf_body_generator.c +++ b/res/res_pjsip_xpidf_body_generator.c @@ -96,7 +96,7 @@ static int xpidf_generate_body_content(void *body, void *data) return 0; } -#define MAX_STRING_GROWTHS 3 +#define MAX_STRING_GROWTHS 5 #define XML_PROLOG 39 static void xpidf_to_string(void *body, struct ast_str **str) |