res_pjsip_mwi: Add voicemail extension and mwi_subscribe_replaces_unsolicited
authorGeorge Joseph <george.joseph@fairview5.com>
Fri, 25 Mar 2016 03:55:03 +0000 (21:55 -0600)
committerGeorge Joseph <george.joseph@fairview5.com>
Wed, 30 Mar 2016 18:23:54 +0000 (13:23 -0500)
res_pjsip_mwi was missing the chan_sip "vmexten" functionality which adds
the Message-Account header to the MWI NOTIFY.  Also, specifying mailboxes
on endpoints for unsolicited mwi and on aors for subscriptions required
that the admin know in advance which the client wanted.  If you specified
mailboxes on the endpoint, subscriptions were rejected even if you also
specified mailboxes on the aor.

Voicemail extension:
* Added a global default_voicemail_extension which defaults to "".
* Added voicemail_extension to both endpoint and aor.
* Added ast_sip_subscription_get_dialog for support.
* Added ast_sip_subscription_get_sip_uri for support.

When an unsolicited NOTIFY is constructed, the From header is parsed, the
voicemail extension from the endpoint is substituted for the user, and the
result placed in the Message-Account field in the body.

When a subscribed NOTIFY is constructed, the subscription dialog local uri
is parsed, the voicemail_extension from the aor (looked up from the
subscription resource name) is substituted for the user, and the result
placed in the Message-Account field in the body.

If no voicemail extension was defined, the Message-Account field is not added
to the NOTIFY body.

mwi_subscribe_replaces_unsolicited:
* Added mwi_subscribe_replaces_unsolicited to endpoint.

The previous behavior was to reject a subscribe if a previous internal
subscription for unsolicited MWI was found for the mailbox.  That remains the
default.  However, if there are mailboxes also set on the aor and the client
subscribes and mwi_subscribe_replaces_unsolicited is set, the existing internal
subscription is removed and replaced with the external subscription.  This
allows an admin to configure mailboxes on both the endpoint and aor and allows
the client to select which to use.

ASTERISK-25865 #close
Reported-by: Ross Beer

Change-Id: Ic15a9415091760539c7134a5ba3dc4a6a1217cea

13 files changed:
configs/samples/pjsip.conf.sample
contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_body_generator_types.h
include/asterisk/res_pjsip_pubsub.h
res/res_pjsip.c
res/res_pjsip/config_global.c
res/res_pjsip/location.c
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_mwi.c
res/res_pjsip_mwi_body_generator.c
res/res_pjsip_pubsub.c
res/res_pjsip_pubsub.exports.in

index faa9285..94d1792 100644 (file)
                         ; "username")
 ;redirect_method=user   ; How redirects received from an endpoint are handled
                         ; (default: "user")
-;mailboxes=     ; Mailbox es to be associated with (default: "")
+;mailboxes=     ; NOTIFY the endpoint when state changes for any of the specified mailboxes.
+                ; Asterisk will send unsolicited MWI NOTIFY messages to the endpoint when state
+                ; changes happen for any of the specified mailboxes. (default: "")
+;voicemail_extension= ; The voicemail extension to send in the NOTIFY Message-Account header
+                      ; (default: global/default_voicemail_extension)
+;mwi_subscribe_replaces_unsolicited=no
+                      ; An MWI subscribe will replace unsoliticed NOTIFYs
+                      ; (default: "no")
 ;moh_suggest=default    ; Default Music On Hold class (default: "default")
 ;moh_passthrough=yes    ; Pass Music On Hold through using SIP re-invites with sendonly
                         ; when placing on hold and sendrecv when taking off hold
 ;default_expiration=3600        ; Default expiration time in seconds for
                                 ; contacts that are dynamically bound to an AoR
                                 ; (default: "3600")
-;mailboxes=     ; Mailbox es to be associated with (default: "")
+;mailboxes=           ; Allow subscriptions for the specified mailbox(es)
+                      ; This option applies when an external entity subscribes to an AoR
+                      ; for Message Waiting Indications. (default: "")
+;voicemail_extension= ; The voicemail extension to send in the NOTIFY Message-Account header
+                      ; (default: global/default_voicemail_extension)
 ;maximum_expiration=7200        ; Maximum time to keep an AoR (default: "7200")
 ;max_contacts=0 ; Maximum number of contacts that can bind to an AoR (default:
                 ; "0")
                             ; startup that qualifies should be attempted on all
                             ; contacts.  If greater than the qualify_frequency
                             ; for an aor, qualify_frequency will be used instead.
-; If regcontext is specified, Asterisk will dynamically create and destroy a
-; NoOp priority 1 extension for a given endpoint who registers or unregisters
-; with us. The extension added is the name of the endpoint.
-;regcontext=sipregistrations
+;regcontext=sipregistrations  ; If regcontext is specified, Asterisk will dynamically
+                              ; create and destroy a NoOp priority 1 extension for a
+                              ; given endpoint who registers or unregisters with us.
+                              ; The extension added is the name of the endpoint.
+;default_voicemail_extension=asterisk
+                   ; The voicemail extension to send in the NOTIFY Message-Account header
+                   ; if not set on endpoint or aor.
+                   ; (default: "")
+
 
 ; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl
 ;==========================ACL SECTION OPTIONS=========================
diff --git a/contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py b/contrib/ast-db-manage/config/versions/1c688d9a003c_pjsip_voicemail_extension.py
new file mode 100644 (file)
index 0000000..781dca7
--- /dev/null
@@ -0,0 +1,31 @@
+"""pjsip voicemail extension
+
+Revision ID: 1c688d9a003c
+Revises: 5813202e92be
+Create Date: 2016-03-24 22:31:45.537895
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '1c688d9a003c'
+down_revision = '5813202e92be'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('default_voicemail_extension', sa.String(40)))
+    op.add_column('ps_aors', sa.Column('voicemail_extension', sa.String(40)))
+    op.add_column('ps_endpoints', sa.Column('voicemail_extension', sa.String(40)))
+    op.add_column('ps_endpoints', sa.Column('mwi_subscribe_replaces_unsolicited', sa.Integer))
+
+
+def downgrade():
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('default_voicemail_extension')
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.drop_column('voicemail_extension')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('voicemail_extension')
+        batch_op.drop_column('mwi_subscribe_replaces_unsolicited')
index b0ae2ce..2c26c25 100644 (file)
@@ -312,6 +312,8 @@ struct ast_sip_aor {
        unsigned int support_path;
        /*! Qualify timeout. 0 is diabled. */
        double qualify_timeout;
+       /* Voicemail extension to set in Message-Account */
+       char *voicemail_extension;
 };
 
 /*!
@@ -466,6 +468,10 @@ struct ast_sip_mwi_configuration {
        );
        /* Should mailbox states be combined into a single notification? */
        unsigned int aggregate;
+       /* Should a subscribe replace unsolicited notifies? */
+       unsigned int subscribe_replaces_unsolicited;
+       /* Voicemail extension to set in Message-Account */
+       char *voicemail_extension;
 };
 
 /*!
@@ -2118,6 +2124,16 @@ char *ast_sip_get_regcontext(void);
 char *ast_sip_get_endpoint_identifier_order(void);
 
 /*!
+ * \brief Retrieve the default voicemail extension.
+ * \since 13.9.0
+ *
+ * \note returned string needs to be de-allocated by caller.
+ *
+ * \retval the default voicemail extension
+ */
+char *ast_sip_get_default_voicemail_extension(void);
+
+/*!
  * \brief Retrieve the global default from user.
  *
  * This is the value placed in outbound requests' From header if there
@@ -2265,4 +2281,5 @@ int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip
 void ast_sip_modify_id_header(pj_pool_t *pool, pjsip_fromto_hdr *id_hdr,
        const struct ast_party_id *id);
 
+
 #endif /* _RES_PJSIP_H */
index a2cc043..aab1472 100644 (file)
@@ -65,6 +65,8 @@ struct ast_sip_message_accumulator {
        int old_msgs;
        /*! Number of new messages */
        int new_msgs;
+       /*! Message-Account */
+       char message_account[PJSIP_MAX_URL_SIZE];
 };
 
 #endif /* _RES_PJSIP_BODY_GENERATOR_TYPES_H */
index c9b66dc..84d86fb 100644 (file)
@@ -339,6 +339,14 @@ struct ast_sip_subscription_handler {
 struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
                struct ast_sip_endpoint *endpoint, const char *resource);
 
+/*!
+ * \brief Get the pjsip dialog that is associated with this subscription
+ * \since 13.9.0
+ *
+ * \retval NULL Could not get dialog
+ * \retval non-NULL The dialog
+ */
+pjsip_dialog *ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub);
 
 /*!
  * \brief Get the endpoint that is associated with this subscription
@@ -379,6 +387,18 @@ struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_sub
 int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data, int terminate);
 
 /*!
+ * \brief Retrieve the local sip uri for this subscription
+ * \since 13.9.0
+ *
+ * This is the local sip URI of the subscribed resource.
+ *
+ * \param sub The subscription
+ * \retval NULL Could not get uri
+ * \retval non-NULL The local pjsip_sip_uri
+ */
+pjsip_sip_uri *ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub);
+
+/*!
  * \brief Retrieve the local URI for this subscription
  *
  * This is the local URI of the subscribed resource.
index b9d6cb6..f4dc725 100644 (file)
                                                configuration.
                                        </para></description>
                                </configOption>
+                               <configOption name="mwi_subscribe_replaces_unsolicited">
+                                       <synopsis>An MWI subscribe will replace sending unsolicited NOTIFYs</synopsis>
+                               </configOption>
+                               <configOption name="voicemail_extension">
+                                       <synopsis>The voicemail extension to send in the NOTIFY Message-Account header</synopsis>
+                               </configOption>
                                <configOption name="moh_suggest" default="default">
                                        <synopsis>Default Music On Hold class</synopsis>
                                </configOption>
                                                endpoint configuration section to enable unsolicited MWI NOTIFYs to the endpoint.
                                        </para></description>
                                </configOption>
+                               <configOption name="voicemail_extension">
+                                       <synopsis>The voicemail extension to send in the NOTIFY Message-Account header</synopsis>
+                               </configOption>
                                <configOption name="maximum_expiration" default="7200">
                                        <synopsis>Maximum time to keep an AoR</synopsis>
                                        <description><para>
                                <configOption name="default_outbound_endpoint" default="default_outbound_endpoint">
                                        <synopsis>Endpoint to use when sending an outbound request to a URI without a specified endpoint.</synopsis>
                                </configOption>
+                               <configOption name="default_voicemail_extension">
+                                       <synopsis>The voicemail extension to send in the NOTIFY Message-Account header if not specified on endpoint or aor</synopsis>
+                               </configOption>
                                <configOption name="debug" default="no">
                                        <synopsis>Enable/Disable SIP debug logging.  Valid options include yes|no or
                                         a host address</synopsis>
index c0fede6..ad03379 100644 (file)
@@ -37,6 +37,7 @@
 #define DEFAULT_FROM_USER "asterisk"
 #define DEFAULT_REGCONTEXT ""
 #define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
+#define DEFAULT_VOICEMAIL_EXTENSION ""
 
 static char default_useragent[256];
 
@@ -52,6 +53,8 @@ struct global_config {
                AST_STRING_FIELD(endpoint_identifier_order);
                /*! User name to place in From header if there is no better option */
                AST_STRING_FIELD(default_from_user);
+               /*! Default voicemail extension */
+               AST_STRING_FIELD(default_voicemail_extension);
        );
        /* Value to put in Max-Forwards header */
        unsigned int max_forwards;
@@ -144,20 +147,35 @@ char *ast_sip_get_debug(void)
 
 char *ast_sip_get_regcontext(void)
 {
-        char *res;
-        struct global_config *cfg;
+       char *res;
+       struct global_config *cfg;
 
-        cfg = get_global_cfg();
-        if (!cfg) {
-                return ast_strdup(DEFAULT_REGCONTEXT);
-        }
+       cfg = get_global_cfg();
+       if (!cfg) {
+               return ast_strdup(DEFAULT_REGCONTEXT);
+       }
 
-        res = ast_strdup(cfg->regcontext);
-        ao2_ref(cfg, -1);
+       res = ast_strdup(cfg->regcontext);
+       ao2_ref(cfg, -1);
 
-        return res;
+       return res;
 }
 
+char *ast_sip_get_default_voicemail_extension(void)
+{
+       char *res;
+       struct global_config *cfg;
+
+       cfg = get_global_cfg();
+       if (!cfg) {
+               return ast_strdup(DEFAULT_VOICEMAIL_EXTENSION);
+       }
+
+       res = ast_strdup(cfg->default_voicemail_extension);
+       ao2_ref(cfg, -1);
+
+       return res;
+}
 
 char *ast_sip_get_endpoint_identifier_order(void)
 {
@@ -347,12 +365,14 @@ int ast_sip_initialize_sorcery_global(void)
                OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
        ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
                OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
+       ast_sorcery_object_field_register(sorcery, "global", "default_voicemail_extension",
+               DEFAULT_VOICEMAIL_EXTENSION, OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config,
+               default_voicemail_extension));
        ast_sorcery_object_field_register(sorcery, "global", "regcontext", DEFAULT_REGCONTEXT,
-                OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
+               OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
        ast_sorcery_object_field_register(sorcery, "global", "contact_expiration_check_interval",
                __stringify(DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL),
-               OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
-
+               OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
 
        if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
                return -1;
index 4008aba..3145dac 100644 (file)
@@ -35,6 +35,7 @@ static void aor_destroy(void *obj)
 
        ao2_cleanup(aor->permanent_contacts);
        ast_string_field_free_memory(aor);
+       ast_free(aor->voicemail_extension);
 }
 
 /*! \brief Allocator for AOR */
@@ -437,6 +438,24 @@ static int contacts_to_var_list(const void *obj, struct ast_variable **fields)
        return 0;
 }
 
+static int voicemail_extension_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+       struct ast_sip_aor *aor = obj;
+
+       aor->voicemail_extension = ast_strdup(var->value);
+
+       return aor->voicemail_extension ? 0 : -1;
+}
+
+static int voicemail_extension_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+       const struct ast_sip_aor *aor = obj;
+
+       *buf = ast_strdup(aor->voicemail_extension);
+
+       return 0;
+}
+
 int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
 {
        char *copy, *name;
@@ -987,6 +1006,7 @@ int ast_sip_initialize_sorcery_location(void)
        ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
        ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, contacts_to_str, contacts_to_var_list, 0, 0);
        ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
+       ast_sorcery_object_field_register_custom(sorcery, "aor", "voicemail_extension", "", voicemail_extension_handler, voicemail_extension_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
        ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
 
index b497f02..baa5063 100644 (file)
@@ -1052,6 +1052,23 @@ static int set_var_to_vl(const void *obj, struct ast_variable **fields)
        return 0;
 }
 
+static int voicemail_extension_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+       struct ast_sip_endpoint *endpoint = obj;
+
+       endpoint->subscription.mwi.voicemail_extension = ast_strdup(var->value);
+
+       return endpoint->subscription.mwi.voicemail_extension ? 0 : -1;
+}
+
+static int voicemail_extension_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+       const struct ast_sip_endpoint *endpoint = obj;
+
+       *buf = ast_strdup(endpoint->subscription.mwi.voicemail_extension);
+
+       return 0;
+}
 
 static void *sip_nat_hook_alloc(const char *name)
 {
@@ -1647,7 +1664,9 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rpid_immediate", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.rpid_immediate));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.mailboxes));
+       ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "voicemail_extension", "", voicemail_extension_handler, voicemail_extension_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.aggregate));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_subscribe_replaces_unsolicited", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.mwi.subscribe_replaces_unsolicited));
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "media_encryption", "no", media_encryption_handler, media_encryption_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_avp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.force_avp));
@@ -1799,6 +1818,7 @@ int ast_res_pjsip_reload_configuration(void)
 static void subscription_configuration_destroy(struct ast_sip_endpoint_subscription_configuration *subscription)
 {
        ast_string_field_free_memory(&subscription->mwi);
+       ast_free(subscription->mwi.voicemail_extension);
 }
 
 static void info_configuration_destroy(struct ast_sip_endpoint_info_configuration *info)
index be38b44..bb8f004 100644 (file)
@@ -42,6 +42,8 @@
 struct mwi_subscription;
 static struct ao2_container *unsolicited_mwi;
 
+static char *default_voicemail_extension;
+
 #define STASIS_BUCKETS 13
 #define MWI_BUCKETS 53
 
@@ -326,11 +328,30 @@ static int get_message_count(void *obj, void *arg, int flags)
        return 0;
 }
 
+static void set_voicemail_extension(pj_pool_t *pool, pjsip_sip_uri *local_uri,
+       struct ast_sip_message_accumulator *counter, const char *voicemail_extension)
+{
+       pjsip_sip_uri *account_uri;
+       const char *vm_exten;
+
+       if (ast_strlen_zero(voicemail_extension)) {
+               vm_exten = default_voicemail_extension;
+       } else {
+               vm_exten = voicemail_extension;
+       }
+
+       if (!ast_strlen_zero(vm_exten)) {
+               account_uri = pjsip_uri_clone(pool, local_uri);
+               pj_strdup2(pool, &account_uri->user, vm_exten);
+               pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, account_uri, counter->message_account, sizeof(counter->message_account));
+       }
+}
+
 struct unsolicited_mwi_data {
        struct mwi_subscription *sub;
        struct ast_sip_endpoint *endpoint;
        pjsip_evsub_state state;
-       const struct ast_sip_body *body;
+       struct ast_sip_message_accumulator *counter;
 };
 
 static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flags)
@@ -339,27 +360,50 @@ 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 struct ast_sip_body *body = mwi_data->body;
        struct ast_sip_contact *contact = obj;
        const char *state_name;
        pjsip_tx_data *tdata;
        pjsip_sub_state_hdr *sub_state;
        pjsip_event_hdr *event;
+       pjsip_from_hdr *from;
+       pjsip_sip_uri *from_uri;
        const pjsip_hdr *allow_events = pjsip_evsub_get_allow_events_hdr(NULL);
+       struct ast_sip_body body;
+       struct ast_str *body_text;
+       struct ast_sip_body_data body_data = {
+               .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
+               .body_data = mwi_data->counter,
+       };
 
        if (ast_sip_create_request("NOTIFY", NULL, endpoint, NULL, contact, &tdata)) {
                ast_log(LOG_WARNING, "Unable to create unsolicited NOTIFY request to endpoint %s URI %s\n", sub->id, contact->uri);
                return 0;
        }
 
-       if (!ast_strlen_zero(endpoint->subscription.mwi.fromuser)) {
-               pjsip_fromto_hdr *from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
-               pjsip_name_addr *from_name_addr = (pjsip_name_addr *) from->uri;
-               pjsip_sip_uri *from_uri = pjsip_uri_get_uri(from_name_addr->uri);
+       body.type = MWI_TYPE;
+       body.subtype = MWI_SUBTYPE;
+       body_text = ast_str_create(64);
+       if (!body_text) {
+               return 0;
+       }
 
+       from = PJSIP_MSG_FROM_HDR(tdata->msg);
+       from_uri = pjsip_uri_get_uri(from->uri);
+
+       if (!ast_strlen_zero(endpoint->subscription.mwi.fromuser)) {
                pj_strdup2(tdata->pool, &from_uri->user, endpoint->subscription.mwi.fromuser);
        }
 
+       set_voicemail_extension(tdata->pool, from_uri, mwi_data->counter, endpoint->subscription.mwi.voicemail_extension);
+
+       if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &body_data, &body_text)) {
+               ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
+               ast_free(body_text);
+               return 0;
+       }
+
+       body.body_text = ast_str_buffer(body_text);
+
        switch (state) {
        case PJSIP_EVSUB_STATE_ACTIVE:
                state_name = "active";
@@ -379,9 +423,11 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) event);
 
        pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, allow_events));
-       ast_sip_add_body(tdata, body);
+       ast_sip_add_body(tdata, &body);
        ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL);
 
+       ast_free(body_text);
+
        return 0;
 }
 
@@ -392,12 +438,6 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
                                "endpoint", sub->id), ao2_cleanup);
        char *endpoint_aors;
        char *aor_name;
-       struct ast_sip_body body;
-       struct ast_str *body_text;
-       struct ast_sip_body_data body_data = {
-               .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
-               .body_data = counter,
-       };
 
        if (!endpoint) {
                ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n",
@@ -410,23 +450,6 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
                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, &body_data, &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",
@@ -438,7 +461,7 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
                struct unsolicited_mwi_data mwi_data = {
                        .sub = sub,
                        .endpoint = endpoint,
-                       .body = &body,
+                       .counter = counter,
                };
 
                if (!aor) {
@@ -454,8 +477,6 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
 
                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)
@@ -463,6 +484,7 @@ static void send_mwi_notify(struct mwi_subscription *sub)
        struct ast_sip_message_accumulator counter = {
                .old_msgs = 0,
                .new_msgs = 0,
+               .message_account[0] = '\0',
        };
        struct ast_sip_body_data data = {
                .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
@@ -472,7 +494,17 @@ static void send_mwi_notify(struct mwi_subscription *sub)
        ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter);
 
        if (sub->is_solicited) {
+               struct ast_sip_aor *aor = ast_sip_location_retrieve_aor(ast_sip_subscription_get_resource_name(sub->sip_sub));
+               pjsip_dialog *dlg = ast_sip_subscription_get_dialog(sub->sip_sub);
+               pjsip_sip_uri *sip_uri = ast_sip_subscription_get_sip_uri(sub->sip_sub);
+
+               if (aor && dlg && sip_uri) {
+                       set_voicemail_extension(dlg->pool, sip_uri, &counter, aor->voicemail_extension);
+               }
+
+               ao2_cleanup(aor);
                ast_sip_subscription_notify(sub->sip_sub, &data, 0);
+
                return;
        }
 
@@ -565,7 +597,12 @@ static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint
 
                mwi_stasis = ao2_find(mwi_sub->stasis_subs, mailbox, OBJ_SEARCH_KEY);
                if (mwi_stasis) {
-                       ret = 1;
+                       if (endpoint->subscription.mwi.subscribe_replaces_unsolicited) {
+                               unsubscribe_stasis(mwi_stasis, NULL, 0);
+                               ao2_unlink(mwi_sub->stasis_subs, mwi_stasis);
+                       } else {
+                               ret = 1;
+                       }
                        ao2_cleanup(mwi_stasis);
                }
        }
@@ -771,6 +808,7 @@ static void *mwi_get_notify_data(struct ast_sip_subscription *sub)
        struct ast_sip_message_accumulator *counter;
        struct mwi_subscription *mwi_sub;
        struct ast_datastore *mwi_datastore;
+       struct ast_sip_aor *aor;
 
        mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE);
        if (!mwi_datastore) {
@@ -784,6 +822,16 @@ static void *mwi_get_notify_data(struct ast_sip_subscription *sub)
                return NULL;
        }
 
+       if ((aor = ast_sip_location_retrieve_aor(ast_sip_subscription_get_resource_name(sub)))) {
+               pjsip_dialog *dlg = ast_sip_subscription_get_dialog(sub);
+               pjsip_sip_uri *sip_uri = ast_sip_subscription_get_sip_uri(sub);
+
+               if (dlg && sip_uri) {
+                       set_voicemail_extension(dlg->pool, sip_uri, counter, aor->voicemail_extension);
+               }
+               ao2_ref(aor, -1);
+       }
+
        ao2_callback(mwi_sub->stasis_subs, OBJ_NODATA, get_message_count, counter);
        ao2_cleanup(mwi_datastore);
        return counter;
@@ -1084,6 +1132,16 @@ static void mwi_startup_event_cb(void *data, struct stasis_subscription *sub, st
        stasis_unsubscribe(sub);
 }
 
+static void global_loaded(const char *object_type)
+{
+       ast_free(default_voicemail_extension);
+       default_voicemail_extension = ast_sip_get_default_voicemail_extension();
+}
+
+static struct ast_sorcery_observer global_observer = {
+       .loaded = global_loaded,
+};
+
 static int reload(void)
 {
        create_mwi_subscriptions();
@@ -1106,6 +1164,8 @@ static int load_module(void)
 
        create_mwi_subscriptions();
        ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
+       ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
+       ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
 
        if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
                ast_sip_push_task(NULL, send_initial_notify_all, NULL);
@@ -1120,8 +1180,10 @@ static int unload_module(void)
 {
        ao2_callback(unsolicited_mwi, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL);
        ao2_ref(unsolicited_mwi, -1);
+       ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
        ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer);
        ast_sip_unregister_subscription_handler(&mwi_handler);
+       ast_free(default_voicemail_extension);
        return 0;
 }
 
index e4b39d5..f46ce04 100644 (file)
@@ -46,7 +46,7 @@ static void *mwi_allocate_body(void *data)
        if (!mwi_str) {
                return NULL;
        }
-       *mwi_str = ast_str_create(64);
+       *mwi_str = ast_str_create(128);
        if (!*mwi_str) {
                ast_free(mwi_str);
                return NULL;
@@ -63,6 +63,9 @@ static int mwi_generate_body_content(void *body, void *data)
                        counter->new_msgs ? "yes" : "no");
        ast_str_append(mwi, 0, "Voice-Message: %d/%d (0/0)\r\n",
                        counter->new_msgs, counter->old_msgs);
+       if (!ast_strlen_zero(counter->message_account))  {
+               ast_str_append(mwi, 0, "Message-Account: %s\r\n", counter->message_account);
+       }
 
        return 0;
 }
index 57ca95d..141c2fc 100644 (file)
@@ -1644,6 +1644,12 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
        return sub;
 }
 
+pjsip_dialog *ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
+{
+       ast_assert(sub->tree->dlg != NULL);
+       return sub->tree->dlg;
+}
+
 struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
 {
        ast_assert(sub->tree->endpoint != NULL);
@@ -2271,6 +2277,11 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
        return res;
 }
 
+pjsip_sip_uri *ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub)
+{
+       return sub->uri;
+}
+
 void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
 {
        pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->uri, buf, size);
index 6616524..a75103b 100644 (file)
@@ -1,44 +1,6 @@
 {
        global:
-               LINKER_SYMBOL_PREFIXast_sip_create_subscription;
-               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;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_accept;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_send_request;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_alloc_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_add_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_get_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_remove_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_register_subscription_handler;
-               LINKER_SYMBOL_PREFIXast_sip_unregister_subscription_handler;
-               LINKER_SYMBOL_PREFIXast_sip_create_publication;
-               LINKER_SYMBOL_PREFIXast_sip_publication_get_endpoint;
-               LINKER_SYMBOL_PREFIXast_sip_publication_get_resource;
-               LINKER_SYMBOL_PREFIXast_sip_publication_get_event_configuration;
-               LINKER_SYMBOL_PREFIXast_sip_publication_create_response;
-               LINKER_SYMBOL_PREFIXast_sip_publication_send_response;
-               LINKER_SYMBOL_PREFIXast_sip_register_publish_handler;
-               LINKER_SYMBOL_PREFIXast_sip_unregister_publish_handler;
-               LINKER_SYMBOL_PREFIXast_sip_publication_add_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_publication_get_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_publication_remove_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_publication_remove_datastore;
-               LINKER_SYMBOL_PREFIXast_sip_pubsub_register_body_generator;
-               LINKER_SYMBOL_PREFIXast_sip_pubsub_unregister_body_generator;
-               LINKER_SYMBOL_PREFIXast_sip_pubsub_register_body_supplement;
-               LINKER_SYMBOL_PREFIXast_sip_pubsub_unregister_body_supplement;
-               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;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_is_terminated;
-               LINKER_SYMBOL_PREFIXast_sip_subscription_destroy;
+               LINKER_SYMBOL_PREFIXast_sip_*;
        local:
                *;
 };