From bc8c08c6090da176afd48712477f4d66fc71ab2b Mon Sep 17 00:00:00 2001 From: Mark Michelson Date: Wed, 25 Jun 2014 20:57:28 +0000 Subject: [PATCH] Abstract PJSIP-specific elements from the pubsub API. This helps to pave the way for RLS work that is to come. Since this is a self-contained change and subscription tests still pass, this work is being committed directly to trunk instead of a working branch. ASTERISK-23865 #close Review: https://reviewboard.asterisk.org/r/3628 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@417233 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/res_pjsip_pubsub.h | 355 +++++-------- res/res_pjsip_exten_state.c | 226 +++----- res/res_pjsip_mwi.c | 293 +++++------ res/res_pjsip_pidf_body_generator.c | 2 +- res/res_pjsip_pubsub.c | 747 +++++++++++++++++---------- res/res_pjsip_pubsub.exports.in | 7 +- res/res_pjsip_xpidf_body_generator.c | 2 +- 7 files changed, 803 insertions(+), 829 deletions(-) diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h index d46cbae30d..bd55a448f9 100644 --- a/include/asterisk/res_pjsip_pubsub.h +++ b/include/asterisk/res_pjsip_pubsub.h @@ -34,6 +34,15 @@ struct ast_datastore_info; */ struct ast_sip_publication; +enum ast_sip_publish_state { + /*! Publication has just been initialized */ + AST_SIP_PUBLISH_STATE_INITIALIZED, + /*! Publication is currently active */ + AST_SIP_PUBLISH_STATE_ACTIVE, + /*! Publication has been terminated */ + AST_SIP_PUBLISH_STATE_TERMINATED, +}; + /*! * \brief Callbacks that publication handlers will define */ @@ -47,61 +56,39 @@ struct ast_sip_publish_handler { /*! * \brief Called when a PUBLISH to establish a new publication arrives. * - * \param endpoint The endpoint from whom the PUBLISH arrived - * \param rdata The PUBLISH request - * \retval NULL PUBLISH was not accepted - * \retval non-NULL New publication - * - * \note The callback is expected to send a response for the PUBLISH in success cases. + * \param endpoint The endpoint from whom the PUBLISH arrived. + * \param resource The resource whose state is being published. + * \return Response code for the incoming PUBLISH */ - struct ast_sip_publication *(*new_publication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata); - - /*! - * \brief Called when a PUBLISH for an existing publication arrives. - * - * This PUBLISH may be intending to change state or it may be simply renewing - * the publication since the publication is nearing expiration. The callback - * is expected to send a response to the PUBLISH. - * - * \param pub The publication on which the PUBLISH arrived - * \param rdata The PUBLISH request - * \retval 0 Publication was accepted - * \retval non-zero Publication was denied - * - * \note The callback is expected to send a response for the PUBLISH. - */ - int (*publish_refresh)(struct ast_sip_publication *pub, pjsip_rx_data *rdata); - + int (*new_publication)(struct ast_sip_endpoint *endpoint, const char *resource); /*! * \brief Called when a publication has reached its expiration. */ void (*publish_expire)(struct ast_sip_publication *pub); - /*! - * \brief Called when a PUBLISH arrives to terminate a publication. + * \brief Published resource has changed states. + * + * The state parameter can be used to take further action. For instance, + * if the state is AST_SIP_PUBLISH_STATE_INITIALIZED, then this is the initial + * PUBLISH request. This is a good time to set up datastores on the publication + * or any other initial needs. + * + * AST_SIP_PUBLISH_STATE_TERMINATED is used when the remote end is terminating + * its publication. This is a good opportunity to free any resources associated with + * the publication. * - * \param pub The publication that is terminating - * \param rdata The PUBLISH request terminating the publication + * AST_SIP_PUBLISH_STATE_ACTIVE is used when a publication that modifies state + * arrives. * - * \note The callback is expected to send a response for the PUBLISH. + * \param pub The publication whose state has changed + * \param body The body of the inbound PUBLISH + * \param state The state of the publication */ - void (*publish_termination)(struct ast_sip_publication *pub, pjsip_rx_data *rdata); - + int (*publication_state_change)(struct ast_sip_publication *pub, pjsip_msg_body *body, + enum ast_sip_publish_state state); AST_LIST_ENTRY(ast_sip_publish_handler) next; }; -/*! - * \brief Create a new publication - * - * Publication handlers should call this when a PUBLISH arrives to establish a new publication. - * - * \param endpoint The endpoint from whom the PUBLISHes arrive - * \param rdata The PUBLISH that established the publication - * \retval NULL Failed to create a publication - * \retval non-NULL The newly-created publication - */ -struct ast_sip_publication *ast_sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata); - /*! * \brief Given a publication, get the associated endpoint * @@ -111,29 +98,6 @@ struct ast_sip_publication *ast_sip_create_publication(struct ast_sip_endpoint * */ struct ast_sip_endpoint *ast_sip_publication_get_endpoint(struct ast_sip_publication *pub); -/*! - * \brief Create a response to an inbound PUBLISH - * - * The created response must be sent using ast_sip_publication_send_response - * - * \param pub The publication - * \param status code The status code to place in the response - * \param rdata The request to which the response is being made - * \param[out] tdata The created response - */ -int ast_sip_publication_create_response(struct ast_sip_publication *pub, int status_code, pjsip_rx_data *rdata, - pjsip_tx_data **tdata); - -/*! - * \brief Send a response for an inbound PUBLISH - * - * \param pub The publication - * \param rdata The request to which the response was made - * \param tdata The response to the request - */ -pj_status_t ast_sip_publication_send_response(struct ast_sip_publication *pub, pjsip_rx_data *rdata, - pjsip_tx_data *tdata); - /*! * \brief Register a publish handler * @@ -222,19 +186,20 @@ struct ast_sip_subscription_response_data { }; #define AST_SIP_MAX_ACCEPT 32 +enum ast_sip_subscription_notify_reason { + /*! Initial NOTIFY for subscription */ + AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED, + /*! Subscription has been renewed */ + AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED, + /*! Subscription is being terminated */ + AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED, + /*! Other unspecified reason */ + AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER +}; -struct ast_sip_subscription_handler { - /*! The name of the event this handler deals with */ - const char *event_name; - /*! The types of body this handler accepts. - * - * \note This option has no bearing when the handler is used in the - * notifier role. When in a subscriber role, this header is used to - * populate the Accept: header of an outbound SUBSCRIBE request - */ - const char *accept[AST_SIP_MAX_ACCEPT]; +struct ast_sip_notifier { /*! - * \brief Default body type defined for the event package this handler handles. + * \brief Default body type defined for the event package this notifier handles. * * Typically, a SUBSCRIBE request will contain one or more Accept headers that tell * what format they expect the body of NOTIFY requests to use. However, every event @@ -243,163 +208,96 @@ struct ast_sip_subscription_handler { */ const char *default_accept; /*! - * \brief Called when a subscription is to be destroyed + * \brief Called when a SUBSCRIBE arrives attempting to establish a new subscription. * - * This is a subscriber and notifier callback. + * The notifier is expected to return the response that should be sent to the + * SUBSCRIBE request. * - * The handler is not expected to send any sort of requests or responses - * during this callback. The handler MUST, however, begin the destruction - * process for the subscription during this callback. - */ - void (*subscription_shutdown)(struct ast_sip_subscription *subscription); - - /*! - * \brief Called when a SUBSCRIBE arrives in order to create a new subscription - * - * This is a notifier callback. - * - * If the notifier wishes to accept the subscription, then it can create - * a new ast_sip_subscription to do so. - * - * If the notifier chooses to create a new subscription, then it must accept - * the incoming subscription using pjsip_evsub_accept() and it must also - * send an initial NOTIFY with the current subscription state. + * If a 200-class response is returned, then the notifier's notify_required + * callback will immediately be called into with a reason of + * AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED. * * \param endpoint The endpoint from which we received the SUBSCRIBE - * \param rdata The SUBSCRIBE request - * \retval NULL The SUBSCRIBE has not been accepted - * \retval non-NULL The newly-created subscription - */ - struct ast_sip_subscription *(*new_subscribe)(struct ast_sip_endpoint *endpoint, - pjsip_rx_data *rdata); - - /*! - * \brief Called when an endpoint renews a subscription. - * - * This is a notifier callback. - * - * Because of the way that the PJSIP evsub framework works, it will automatically - * send a response to the SUBSCRIBE. However, the subscription handler must send - * a NOTIFY with the current subscription state when this callback is called. - * - * The response_data that is passed into this callback is used to craft what should - * be in the response to the incoming SUBSCRIBE. It is initialized with a 200 status - * code and all other parameters are empty. - * - * \param sub The subscription that is being renewed - * \param rdata The SUBSCRIBE request in question - * \param[out] response_data Data pertaining to the SIP response that should be - * sent to the SUBSCRIBE + * \param resource The name of the resource to which the subscription is being made + * \return The response code to send to the SUBSCRIBE. */ - void (*resubscribe)(struct ast_sip_subscription *sub, - pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data); - + int (*new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource); /*! - * \brief Called when a subscription times out. + * \brief The subscription is in need of a NOTIFY request. * - * This is a notifier callback + * A reason of AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED is given immediately + * after a SUBSCRIBE is accepted. This is a good opportunity for the notifier to + * perform setup duties such as establishing Stasis subscriptions or adding + * datastores to the subscription. * - * This indicates that the subscription has timed out. The subscription handler is - * expected to send a NOTIFY that terminates the subscription. + * A reason of AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED is given when the + * subscriber has terminated the subscription. If there are any duties that the * - * \param sub The subscription that has timed out - */ - void (*subscription_timeout)(struct ast_sip_subscription *sub); - - /*! - * \brief Called when a subscription is terminated via a SUBSCRIBE or NOTIFY request - * - * This is a notifier and subscriber callback. - * - * The PJSIP subscription framework will automatically send the response to the - * request. If a notifier receives this callback, then the subscription handler - * is expected to send a final NOTIFY to terminate the subscription. * - * \param sub The subscription being terminated - * \param rdata The request that terminated the subscription - */ - void (*subscription_terminated)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); - - /*! - * \brief Called when a subscription handler's outbound NOTIFY receives a response - * - * This is a notifier callback. - * - * \param sub The subscription - * \param rdata The NOTIFY response + * \param sub The subscription to send the NOTIFY on. + * \param reason The reason why the NOTIFY is being sent. + * \retval 0 Success + * \retval -1 Failure */ - void (*notify_response)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); + int (*notify_required)(struct ast_sip_subscription *sub, enum ast_sip_subscription_notify_reason reason); +}; +struct ast_sip_subscriber { /*! - * \brief Called when a subscription handler receives an inbound NOTIFY - * - * This is a subscriber callback. + * \brief A NOTIFY has been received. * - * Because of the way that the PJSIP evsub framework works, it will automatically - * send a response to the NOTIFY. By default this will be a 200 OK response, but - * this callback can change details of the response by returning response data - * to use. + * The body of the NOTIFY is provided so that it may be parsed and appropriate + * internal state change may be generated. * - * The response_data that is passed into this callback is used to craft what should - * be in the response to the incoming SUBSCRIBE. It is initialized with a 200 status - * code and all other parameters are empty. + * The state can be used to determine if the subscription has been terminated + * by the far end or if this is just a typical resource state change. * - * \param sub The subscription - * \param rdata The NOTIFY request - * \param[out] response_data Data pertaining to the SIP response that should be - * sent to the SUBSCRIBE + * \param sub The subscription on which the NOTIFY arrived + * \param body The body of the NOTIFY + * \param state The subscription state */ - void (*notify_request)(struct ast_sip_subscription *sub, - pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data); + void (*state_change)(struct ast_sip_subscription *sub, pjsip_msg_body *body, enum pjsip_evsub_state state); +}; +struct ast_sip_subscription_handler { + /*! The name of the event this subscriber deals with */ + const char *event_name; + /*! The types of body this subscriber accepts. */ + const char *accept[AST_SIP_MAX_ACCEPT]; /*! - * \brief Called when it is time for a subscriber to resubscribe - * - * This is a subscriber callback. - * - * The subscriber can reresh the subscription using the pjsip_evsub_initiate() - * function. + * \brief Called when a subscription is to be destroyed * - * \param sub The subscription to refresh - * \retval 0 Success - * \retval non-zero Failure + * The handler is not expected to send any sort of requests or responses + * during this callback. The handler MUST, however, begin the destruction + * process for the subscription during this callback. */ - int (*refresh_subscription)(struct ast_sip_subscription *sub); - + void (*subscription_shutdown)(struct ast_sip_subscription *subscription); /*! * \brief Converts the subscriber to AMI * - * This is a subscriber callback. - * * \param sub The subscription * \param buf The string to write AMI data */ void (*to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf); + /*! Subscriber callbacks for this handler */ + struct ast_sip_subscriber *subscriber; + /*! Notifier callbacks for this handler */ + struct ast_sip_notifier *notifier; AST_LIST_ENTRY(ast_sip_subscription_handler) next; }; /*! * \brief Create a new ast_sip_subscription structure * - * In most cases the pubsub core will create a general purpose subscription - * within PJSIP. However, PJSIP provides enhanced support for the following - * event packages: - * - * presence - * message-summary + * When a subscriber wishes to create a subscription, it may call this function + * to allocate resources and to send the initial SUBSCRIBE out. * - * If either of these events are handled by the subscription handler, then - * the special-purpose event subscriptions will be created within PJSIP, - * and it will be expected that your subscription handler make use of the - * special PJSIP APIs. - * - * \param handler The subsription handler for this subscription - * \param role Whether we are acting as subscriber or notifier for this subscription - * \param endpoint The endpoint involved in this subscription - * \param rdata If acting as a notifier, the SUBSCRIBE request that triggered subscription creation + * \param subscriber The subscriber that is making the request. + * \param endpoint The endpoint to whome the SUBSCRIBE will be sent. + * \param resource The resource to place in the SUBSCRIBE's Request-URI. */ 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); + struct ast_sip_endpoint *endpoint, const char *resource); /*! @@ -426,46 +324,61 @@ struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscr struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub); /*! - * \brief Get the underlying PJSIP evsub structure + * \brief Notify a SIP subscription of a state change. * - * This is useful when wishing to call PJSIP's API calls in order to - * create SUBSCRIBEs, NOTIFIES, etc. as well as get subscription state + * This will create a NOTIFY body to be sent out for the subscribed resource. + * On real subscriptions, a NOTIFY request will be generated and sent. + * On virtual subscriptions, the NOTIFY is saved on the virtual subscription and the + * parent subscription is alerted. * - * This function, as well as all methods called on the pjsip_evsub should - * be done in a SIP servant thread. + * \param sub The subscription on which a state change is occurring. + * \param notify_data Event package-specific data used to create the NOTIFY body. + * \param terminate True if this NOTIFY is intended to terminate the subscription. + * \retval 0 Success + * \retval non-zero Failure + */ +int ast_sip_subscription_notify(struct ast_sip_subscription *sub, void *notify_data, int terminate); + +/*! + * \brief Retrieve the local URI for this subscription + * + * This is the local URI as determined by the underlying SIP dialog. * * \param sub The subscription - * \retval NULL Failure - * \retval non-NULL The underlying pjsip_evsub + * \param[out] buf The buffer into which to store the URI. + * \param size The size of the buffer. */ -pjsip_evsub *ast_sip_subscription_get_evsub(struct ast_sip_subscription *sub); +void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size); /*! - * \brief Get the underlying PJSIP dialog structure - * - * Call this function when information needs to be retrieved from the - * underlying pjsip dialog. + * \brief Retrive the remote URI for this subscription * - * This function, as well as all methods called on the pjsip_evsub should - * be done in a SIP servant thread. + * This is the remote URI as determined by the underlying SIP dialog. * * \param sub The subscription - * \retval NULL Failure - * \retval non-NULL The underlying pjsip_dialog + * \param[out] buf The buffer into which to store the URI. + * \param size The size of the buffer. + */ +void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size); + +/*! + * \brief Get the name of the subscribed resource. */ -pjsip_dialog *ast_sip_subscription_get_dlg(struct ast_sip_subscription *sub); +const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub); /*! - * \brief Accept a subscription request + * \brief Get a header value for a subscription. * - * \param sub The subscription to be accepted - * \param rdata The received subscription request - * \param response The response code to send + * For notifiers, the headers of the inbound SUBSCRIBE that started the dialog + * are stored on the subscription. This method allows access to the header. The + * return is the same as pjsip_msg_find_hdr_by_name(), meaning that it is dependent + * on the header being searched for. * - * \retval 0 Success - * \retval non-zero Failure + * \param sub The subscription to search in. + * \param header The name of the header to search for. + * \return The discovered header, or NULL if the header cannot be found. */ -int ast_sip_subscription_accept(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, int response); +void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header); /*! * \brief Send a request created via a PJSIP evsub method diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c index f4bfef772e..cb39026ca2 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 f25a7c48f9..a8e2d14297 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 875a65fda4..690051e136 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 dfca643bc1..39846823e4 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 }; @@ -248,6 +248,55 @@ struct subscription_persistence { struct timeval expires; }; +/*! + * \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 */ @@ -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; } @@ -478,13 +562,12 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags) return 0; } - 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 0877d5c6cb..ca165af927 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 fba6152b3c..aeb313f12d 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)