Merge "Scripts: check file versions of Asterisk and dependencies"
authorJoshua Colp <jcolp@digium.com>
Mon, 28 Sep 2015 11:50:12 +0000 (06:50 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Mon, 28 Sep 2015 11:50:12 +0000 (06:50 -0500)
49 files changed:
CHANGES
addons/chan_ooh323.c
addons/ooh323c/src/ooq931.c
apps/app_dial.c
apps/app_page.c
apps/app_queue.c
apps/app_record.c
cdr/cdr_adaptive_odbc.c
cel/cel_odbc.c
cel/cel_pgsql.c
channels/chan_pjsip.c
channels/chan_sip.c
channels/pjsip/dialplan_functions.c
contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
include/asterisk/stasis_app.h
main/astfd.c
main/channel.c
main/config_options.c
main/dial.c
main/endpoints.c
main/logger.c
main/pbx.c
main/sched.c
main/stasis_endpoints.c
main/utils.c
res/ari/ari_model_validators.c
res/ari/ari_model_validators.h
res/ari/resource_events.c
res/ari/resource_events.h
res/parking/parking_applications.c
res/res_ari_events.c
res/res_config_sqlite.c
res/res_pjsip.c
res/res_pjsip/config_global.c
res/res_pjsip/location.c
res/res_pjsip_diversion.c
res/res_pjsip_nat.c
res/res_pjsip_pubsub.c
res/res_pjsip_session.c
res/res_pjsip_transport_websocket.c
res/res_rtp_asterisk.c
res/res_stasis.c
res/res_stasis_device_state.c
res/stasis/app.c
res/stasis/app.h
res/stasis/messaging.c
rest-api-templates/ari_model_validators.c.mustache
rest-api/api-docs/events.json

diff --git a/CHANGES b/CHANGES
index 322419d..9d599f4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -177,6 +177,38 @@ cdr_adaptive_odbc
    names. This setting is configurable for cdr_adaptive_odbc via the
    quoted_identifiers in configuration file cdr_adaptive_odbc.conf.
 
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.5.0 to Asterisk 13.6.0 ------------
+------------------------------------------------------------------------------
+
+Dialplan Functions
+------------------
+ * The CHANNEL function, when used on a PJSIP channel, now exposes a 'call-id'
+   extraction option when using with the 'pjsip' signalling option. It will
+   return the SIP Call-ID associated with the INVITE request that established
+   the PJSIP channel.
+
+ARI
+------------------
+ * Two new endpoint related events are now available: PeerStatusChange and
+   ContactStatusChange. In particular, these events are useful when subscribing
+   to all event sources, as they provide additional endpoint related
+   information beyond the addition/removal of channels from an endpoint.
+
+ * Added the ability to subscribe to all ARI events in Asterisk, regardless
+   of whether the application 'controls' the resource. This is useful for
+   scenarios where an ARI application merely wants to observe the system,
+   as opposed to control it. There are two ways to accomplish this:
+   (1) Via the WebSocket connection URI. A new query paramter, 'subscribeAll',
+       has been added that, when present and True, will subscribe all
+       specified applications to all ARI event sources in Asterisk.
+   (2) Via the applications resource. An ARI client can, at any time, subscribe
+       to all resources in an event source merely by not providing an explicit
+       resource. For example, subscribing to an event source of 'channels:'
+       as opposed to 'channels:12345' will subscribe the application to all
+       channels.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.4.0 to Asterisk 13.5.0 ------------
 ------------------------------------------------------------------------------
index 58db56f..aa9cbaf 100644 (file)
@@ -4115,6 +4115,7 @@ int ooh323_destroy(struct ooh323_pvt *p)
                }
 
                if (cur->rtp) {
+                       ast_rtp_instance_stop(cur->rtp);
                        ast_rtp_instance_destroy(cur->rtp);
                        cur->rtp = NULL;
                }
index 86ecd92..cbc4afb 100644 (file)
@@ -758,14 +758,14 @@ int ooEncodeH225Message(OOH323CallData *call, Q931Message *pq931Msg,
       i += pq931Msg->causeIE->length;
    } 
       
-   /*Add progress indicator IE 
-   if(pq931Msg->messageType == Q931AlertingMsg || pq931Msg->messageType == Q931CallProceedingMsg)
+   /* Add progress indicator IE */
+   if(pq931Msg->messageType == Q931AlertingMsg || pq931Msg->messageType == Q931ProgressMsg)
    {
       msgbuf[i++] = Q931ProgressIndicatorIE;
       msgbuf[i++] = 2; //Length is 2 octet
       msgbuf[i++] = 0x80; //PI=8
       msgbuf[i++] = 0x88;
-  }*/
+   }
 
    /*Add display ie. for all but Status message as per ASTERISK-18748 */
    if(!ooUtilsIsStrEmpty(call->ourCallerId) && (pq931Msg->messageType != Q931StatusMsg))
index 46014c6..f646bba 100644 (file)
@@ -2707,7 +2707,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                        chans[0] = chan;
                        chans[1] = peer;
 
-                       /* we need to stream the announcment while monitoring the caller for a hangup */
+                       /* we need to stream the announcement while monitoring the caller for a hangup */
 
                        /* stream the file */
                        res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], ast_channel_language(peer));
@@ -2786,7 +2786,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                        if (ast_pbx_start(peer)) {
                                ast_autoservice_chan_hangup_peer(chan, peer);
                        }
-                       hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
                        if (continue_exec)
                                *continue_exec = 1;
                        res = 0;
index cea75cb..10a96b6 100644 (file)
@@ -249,12 +249,18 @@ static void page_state_callback(struct ast_dial *dial)
 
 static int page_exec(struct ast_channel *chan, const char *data)
 {
-       char *tech, *resource, *tmp;
-       char confbridgeopts[128], originator[AST_CHANNEL_NAME];
+       char *tech;
+       char *resource;
+       char *tmp;
+       char *predial_callee = NULL;
+       char confbridgeopts[128];
+       char originator[AST_CHANNEL_NAME];
        struct page_options options = { { 0, }, { 0, } };
        unsigned int confid = ast_random();
        struct ast_app *app;
-       int res = 0, pos = 0, i = 0;
+       int res = 0;
+       int pos = 0;
+       int i = 0;
        struct ast_dial **dial_list;
        unsigned int num_dials;
        int timeout = 0;
@@ -310,6 +316,15 @@ static int page_exec(struct ast_channel *chan, const char *data)
                return -1;
        }
 
+       /* PREDIAL: Preprocess any callee gosub arguments. */
+       if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
+               && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
+               ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLEE]);
+               predial_callee =
+                       (char *) ast_app_expand_sub_args(chan, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+       }
+
+       /* PREDIAL: Run gosub on the caller's channel */
        if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLER)
                && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLER])) {
                ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLER]);
@@ -360,9 +375,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
                /* Set ANSWER_EXEC as global option */
                ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, confbridgeopts);
 
-               if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
-                       && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
-                       ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+               if (predial_callee) {
+                       ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, predial_callee);
                }
 
                if (timeout) {
@@ -383,6 +397,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
                dial_list[pos++] = dial;
        }
 
+       ast_free(predial_callee);
+
        if (!ast_test_flag(&options.flags, PAGE_QUIET)) {
                res = ast_streamfile(chan, "beep", ast_channel_language(chan));
                if (!res)
index bb3ec17..23f6985 100644 (file)
@@ -5582,6 +5582,10 @@ struct queue_stasis_data {
        struct local_optimization caller_optimize;
        /*! Local channel optimization details for the member */
        struct local_optimization member_optimize;
+       /*! Member channel */
+       struct ast_channel *member_channel;
+       /*! Caller channel */
+       struct ast_channel *caller_channel;
 };
 
 /*!
@@ -5599,6 +5603,9 @@ static void queue_stasis_data_destructor(void *obj)
        ao2_cleanup(queue_data->member);
        queue_unref(queue_data->queue);
        ast_string_field_free_memory(queue_data);
+
+       ao2_ref(queue_data->member_channel, -1);
+       ao2_ref(queue_data->caller_channel, -1);
 }
 
 /*!
@@ -5645,6 +5652,16 @@ static struct queue_stasis_data *queue_stasis_data_alloc(struct queue_ent *qe,
        queue_data->caller_pos = qe->opos;
        ao2_ref(mem, +1);
        queue_data->member = mem;
+
+       /*
+        * During transfers it's possible for both the member and/or caller
+        * channel(s) to not be available. Adding a reference here ensures
+        * that the channels remain until app_queue is completely done with
+        * them.
+        */
+       queue_data->member_channel = ao2_bump(peer);
+       queue_data->caller_channel = ao2_bump(qe->chan);
+
        return queue_data;
 }
 
@@ -6016,7 +6033,9 @@ static void handle_hangup(void *userdata, struct stasis_subscription *sub,
        }
 
        chan = ast_channel_get_by_name(channel_blob->snapshot->name);
-       if (chan && ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME)) {
+       if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
+                    !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
+                    !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
                /* Channel that is hanging up is doing it as part of a transfer.
                 * We'll get a transfer event later
                 */
index ed54a77..fcd8938 100644 (file)
@@ -294,9 +294,10 @@ static int record_exec(struct ast_channel *chan, const char *data)
                        }
                        count++;
                } while (ast_fileexists(tmp, ext, ast_channel_language(chan)) > 0);
-               pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
        } else
                ast_copy_string(tmp, args.filename, sizeof(tmp));
+
+       pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
        /* end of routine mentioned */
 
        if (ast_channel_state(chan) != AST_STATE_UP) {
index 8bd9d0d..0da8c01 100644 (file)
@@ -358,11 +358,11 @@ static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
        return stmt;
 }
 
-#define LENGTHEN_BUF1(size)                                                                                                            \
+#define LENGTHEN_BUF(size, var_sql)                                                                                                            \
                        do {                                                                                                                            \
                                /* Lengthen buffer, if necessary */                                                             \
-                               if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) {               \
-                                       if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \
+                               if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) {               \
+                                       if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 1) / 512 + 1) * 512) != 0) { \
                                                ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
                                                ast_free(sql);                                                                                  \
                                                ast_free(sql2);                                                                                 \
@@ -372,18 +372,10 @@ static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
                                }                                                                                                                               \
                        } while (0)
 
-#define LENGTHEN_BUF2(size)                                                                                                            \
-                       do {                                                                                                                            \
-                               if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) {             \
-                                       if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
-                                               ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
-                                               ast_free(sql);                                                                                  \
-                                               ast_free(sql2);                                                                                 \
-                                               AST_RWLIST_UNLOCK(&odbc_tables);                                                \
-                                               return -1;                                                                                              \
-                                       }                                                                                                                       \
-                               }                                                                                                                               \
-                       } while (0)
+#define LENGTHEN_BUF1(size)    \
+       LENGTHEN_BUF(size, sql);
+#define LENGTHEN_BUF2(size)    \
+       LENGTHEN_BUF(size, sql2);
 
 static int odbc_log(struct ast_cdr *cdr)
 {
index 2d8408b..3c0be04 100644 (file)
@@ -341,11 +341,11 @@ static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
        return stmt;
 }
 
-#define LENGTHEN_BUF1(size)                                                                                                            \
+#define LENGTHEN_BUF(size, var_sql)                                                                                                            \
                        do {                                                                                                                            \
                                /* Lengthen buffer, if necessary */                                                             \
-                               if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) {               \
-                                       if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \
+                               if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) {               \
+                                       if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 1) / 512 + 1) * 512) != 0) { \
                                                ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
                                                ast_free(sql);                                                                                  \
                                                ast_free(sql2);                                                                                 \
@@ -355,18 +355,11 @@ static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
                                }                                                                                                                               \
                        } while (0)
 
-#define LENGTHEN_BUF2(size)                                                                                                            \
-                       do {                                                                                                                            \
-                               if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) {             \
-                                       if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
-                                               ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
-                                               ast_free(sql);                                                                                  \
-                                               ast_free(sql2);                                                                                 \
-                                               AST_RWLIST_UNLOCK(&odbc_tables);                                                \
-                                               return;                                                                                                 \
-                                       }                                                                                                                       \
-                               }                                                                                                                               \
-                       } while (0)
+#define LENGTHEN_BUF1(size) \
+       LENGTHEN_BUF(size, sql);
+
+#define LENGTHEN_BUF2(size) \
+       LENGTHEN_BUF(size, sql2);
 
 static void odbc_log(struct ast_event *event)
 {
index f495947..e4e280e 100644 (file)
@@ -100,11 +100,11 @@ struct columns {
 
 static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
 
-#define LENGTHEN_BUF1(size) \
+#define LENGTHEN_BUF(size, var_sql) \
        do { \
                /* Lengthen buffer, if necessary */ \
-               if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
-                       if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
+               if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) { \
+                       if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 3) / 512 + 1) * 512) != 0) { \
                                ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", pghostname, table); \
                                ast_free(sql); \
                                ast_free(sql2); \
@@ -114,18 +114,10 @@ static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
                } \
        } while (0)
 
+#define LENGTHEN_BUF1(size) \
+       LENGTHEN_BUF(size, sql);
 #define LENGTHEN_BUF2(size) \
-       do { \
-               if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
-                       if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
-                               ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", pghostname, table); \
-                               ast_free(sql); \
-                               ast_free(sql2); \
-                               AST_RWLIST_UNLOCK(&psql_columns); \
-                               return; \
-                       } \
-               } \
-       } while (0)
+       LENGTHEN_BUF(size, sql2);
 
 static void pgsql_reconnect(void)
 {
index 9603f05..153b2a3 100644 (file)
@@ -160,10 +160,10 @@ static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
 static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
 {
        struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
-       struct chan_pjsip_pvt *pvt = channel->pvt;
+       struct chan_pjsip_pvt *pvt;
        struct ast_sip_endpoint *endpoint;
 
-       if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
+       if (!channel || !channel->session || !(pvt = channel->pvt) || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
                return AST_RTP_GLUE_RESULT_FORBID;
        }
 
index e385ddb..8d5af2f 100644 (file)
@@ -13940,6 +13940,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
        int cid_has_name = 1;
        int cid_has_num = 1;
        struct ast_party_id connected_id;
+       int ret;
 
        if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
                const char *s = p->username;    /* being a string field, cannot be NULL */
@@ -14024,26 +14025,41 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
 
        ast_copy_string(tmp_l, l, sizeof(tmp_l));
        if (sip_cfg.pedanticsipchecking) {
-               ast_escape_quoted(n, tmp_n, sizeof(tmp_n));
-               n = tmp_n;
                ast_uri_encode(l, tmp_l, sizeof(tmp_l), ast_uri_sip_user);
        }
 
        ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
 
-       /* If a caller id name was specified, add a display name. */
-       if (cid_has_name || !cid_has_num) {
-               snprintf(from, sizeof(from), "\"%s\" ", n);
+       if (!sip_standard_port(p->socket.type, ourport)) {
+               ret = snprintf(from, sizeof(from), "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
        } else {
-               from[0] = '\0';
+               ret = snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+       }
+       if (ret < 0 || ret >= sizeof(from)) { /* a return value of size or more means that the output was truncated */
+               /* We don't have an escape path from here... */
+               ast_log(LOG_ERROR, "The From header was truncated in call '%s'. This call setup will fail.\n", p->callid);
        }
 
-       if (!sip_standard_port(p->socket.type, ourport)) {
-               size_t offset = strlen(from);
-               snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
-       } else {
-               size_t offset = strlen(from);
-               snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+       /* If a caller id name was specified, prefix a display name, if there is enough room. */
+       if (cid_has_name || !cid_has_num) {
+               size_t written = strlen(from);
+               ssize_t left = sizeof(from) - written - 4; /* '"" \0' */
+               if (left > 0) {
+                       size_t name_len;
+                       if (sip_cfg.pedanticsipchecking) {
+                               ast_escape_quoted(n, tmp_n, MIN(left + 1, sizeof(tmp_n)));
+                               n = tmp_n;
+                       }
+                       name_len = strlen(n);
+                       if (left < name_len) {
+                               name_len = left;
+                       }
+                       memmove(from + name_len + 3, from, written + 1);
+                       from[0] = '"';
+                       memcpy(from + 1, n, name_len);
+                       from[name_len + 1] = '"';
+                       from[name_len + 2] = ' ';
+               }
        }
 
        if (!ast_strlen_zero(explicit_uri)) {
@@ -14085,21 +14101,25 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
                /*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
                if (!strchr(p->todnid, '@')) {
                        /* We have no domain in the dnid */
-                       snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+                       ret = snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
                } else {
-                       snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+                       ret = snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
                }
        } else {
                if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
                        /* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
-                       snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
+                       ret = snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
                } else if (p->options && p->options->vxml_url) {
                        /* If there is a VXML URL append it to the SIP URL */
-                       snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
+                       ret = snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
                } else {
-                       snprintf(to, sizeof(to), "<%s>", p->uri);
+                       ret = snprintf(to, sizeof(to), "<%s>", p->uri);
                }
        }
+       if (ret < 0 || ret >= sizeof(to)) { /* a return value of size or more means that the output was truncated */
+               /* We don't have an escape path from here... */
+               ast_log(LOG_ERROR, "The To header was truncated in call '%s'. This call setup will fail.\n", p->callid);
+       }
 
        init_req(req, sipmethod, p->uri);
        /* now tmp_n is available so reuse it to build the CSeq */
@@ -33152,10 +33172,8 @@ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
 /*! \brief  Part of Asterisk module interface */
 static int reload(void)
 {
-       if (sip_reload(0, 0, NULL)) {
-               return 0;
-       }
-       return 1;
+       sip_reload(0, 0, NULL);
+       return AST_MODULE_LOAD_SUCCESS;
 }
 
 /*! \brief  Return the first entry from ast_sockaddr_resolve filtered by address family
index 24daac8..b86cfad 100644 (file)
                                <literal>type</literal> parameter must be provided. It specifies
                                which signalling parameter to read.</para>
                                <enumlist>
+                                       <enum name="call-id">
+                                               <para>The SIP call-id.</para>
+                                       </enum>
                                        <enum name="secure">
                                                <para>Whether or not the signalling uses a secure transport.</para>
                                                <enumlist>
@@ -594,6 +597,8 @@ static int channel_read_pjsip(struct ast_channel *chan, const char *type, const
        if (ast_strlen_zero(type)) {
                ast_log(LOG_WARNING, "You must supply a type field for 'pjsip' information\n");
                return -1;
+       } else if (!strcmp(type, "call-id")) {
+               snprintf(buf, buflen, "%.*s", (int) pj_strlen(&dlg->call_id->id), pj_strbuf(&dlg->call_id->id));
        } else if (!strcmp(type, "secure")) {
 #ifdef HAVE_PJSIP_GET_DEST_INFO
                pjsip_host_info dest;
diff --git a/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py b/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py
new file mode 100644 (file)
index 0000000..7e6cf99
--- /dev/null
@@ -0,0 +1,22 @@
+"""add default_from_user
+
+Revision ID: 154177371065
+Revises: 26f10cadc157
+Create Date: 2015-09-04 14:13:59.195013
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '154177371065'
+down_revision = '26f10cadc157'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('default_from_user', sa.String(80)))
+
+
+def downgrade():
+    op.drop_column('ps_globals', 'default_from_user')
index 508a7be..29edbd2 100644 (file)
@@ -2018,6 +2018,19 @@ char *ast_sip_get_debug(void);
  */
 char *ast_sip_get_endpoint_identifier_order(void);
 
+/*!
+ * \brief Retrieve the global default from user.
+ *
+ * This is the value placed in outbound requests' From header if there
+ * is no better option (such as an endpoint-configured from_user or
+ * caller ID number).
+ *
+ * \param[out] from_user The default from user
+ * \param size The buffer size of from_user
+ * \return nothing
+ */
+void ast_sip_get_default_from_user(char *from_user, size_t size);
+
 /*! \brief Determines whether the res_pjsip module is loaded */
 #define CHECK_PJSIP_MODULE_LOADED()                            \
        do {                                                    \
index 567670b..f2b07e0 100644 (file)
@@ -92,6 +92,21 @@ struct ao2_container *stasis_app_get_all(void);
 int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data);
 
 /*!
+ * \brief Register a new Stasis application that receives all Asterisk events.
+ *
+ * If an application is already registered with the given name, the old
+ * application is sent a 'replaced' message and unregistered.
+ *
+ * \param app_name Name of this application.
+ * \param handler Callback for application messages.
+ * \param data Data blob to pass to the callback. Must be AO2 managed.
+ *
+ * \return 0 for success
+ * \return -1 for error.
+ */
+int stasis_app_register_all(const char *app_name, stasis_app_cb handler, void *data);
+
+/*!
  * \brief Unregister a Stasis application.
  * \param app_name Name of the application to unregister.
  */
index 7467376..d1879f0 100644 (file)
@@ -46,6 +46,8 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/lock.h"
 #include "asterisk/strings.h"
 #include "asterisk/unaligned.h"
+#include "asterisk/localtime.h"
+#include "asterisk/time.h"
 
 static struct fdleaks {
        const char *callname;
@@ -54,6 +56,7 @@ static struct fdleaks {
        char file[40];
        char function[25];
        char callargs[60];
+       struct timeval now;
 } fdleaks[1024] = { { "", }, };
 
 /* COPY does ast_copy_string(dst, src, sizeof(dst)), except:
@@ -79,6 +82,7 @@ static struct fdleaks {
 #define STORE_COMMON(offset, name, ...)     \
        do { \
                struct fdleaks *tmp = &fdleaks[offset]; \
+               tmp->now = ast_tvnow(); \
                COPY(tmp->file, file);      \
                tmp->line = line;           \
                COPY(tmp->function, func);  \
@@ -271,7 +275,7 @@ static char *handle_show_fd(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
        case CLI_GENERATE:
                return NULL;
        }
-       getrlimit(RLIMIT_FSIZE, &rl);
+       getrlimit(RLIMIT_NOFILE, &rl);
        if (rl.rlim_cur == RLIM_INFINITY || rl.rlim_max == RLIM_INFINITY) {
                ast_copy_string(line, "unlimited", sizeof(line));
        } else {
@@ -280,8 +284,13 @@ static char *handle_show_fd(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
        ast_cli(a->fd, "Current maxfiles: %s\n", line);
        for (i = 0; i < ARRAY_LEN(fdleaks); i++) {
                if (fdleaks[i].isopen) {
+                       struct ast_tm tm = {0};
+                       char datestring[256];
+
+                       ast_localtime(&fdleaks[i].now, &tm, NULL);
+                       ast_strftime(datestring, sizeof(datestring), "%F %T", &tm);
                        snprintf(line, sizeof(line), "%d", fdleaks[i].line);
-                       ast_cli(a->fd, "%5d %15s:%-7.7s (%-25s): %s(%s)\n", i, fdleaks[i].file, line, fdleaks[i].function, fdleaks[i].callname, fdleaks[i].callargs);
+                       ast_cli(a->fd, "%5d [%s] %22s:%-7.7s (%-25s): %s(%s)\n", i, datestring, fdleaks[i].file, line, fdleaks[i].function, fdleaks[i].callname, fdleaks[i].callargs);
                }
        }
        return CLI_SUCCESS;
index 25badf3..3e2636f 100644 (file)
@@ -4487,7 +4487,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
        /* Don't bother if the channel is about to go away, anyway. */
        if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
-                       || ast_check_hangup(chan))
+                       || (ast_check_hangup(chan) && !ast_channel_is_leaving_bridge(chan)))
                && condition != AST_CONTROL_MASQUERADE_NOTIFY) {
                res = -1;
                goto indicate_cleanup;
index a1ba5d2..4ab7a5b 100644 (file)
@@ -837,6 +837,10 @@ int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
        struct aco_option *opt;
        struct ao2_iterator iter;
 
+       if (!type->internal) {
+               return -1;
+       }
+
        iter = ao2_iterator_init(type->internal->opts, 0);
 
        while ((opt = ao2_iterator_next(&iter))) {
index b935b6d..34d2f70 100644 (file)
@@ -378,14 +378,13 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
        ast_channel_unlock(channel->owner);
 
        if (!ast_strlen_zero(predial_string)) {
-               const char *predial_callee = ast_app_expand_sub_args(chan, predial_string);
-               if (!predial_callee) {
-                       ast_log(LOG_ERROR, "Could not expand subroutine arguments in predial request '%s'\n", predial_string);
+               if (chan) {
+                       ast_autoservice_start(chan);
+               }
+               ast_pre_call(channel->owner, predial_string);
+               if (chan) {
+                       ast_autoservice_stop(chan);
                }
-               ast_autoservice_start(chan);
-               ast_pre_call(channel->owner, predial_callee);
-               ast_autoservice_stop(chan);
-               ast_free((char *) predial_callee);
        }
 
        return 0;
@@ -397,10 +396,6 @@ int ast_dial_prerun(struct ast_dial *dial, struct ast_channel *chan, struct ast_
        int res = -1;
        char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-       if (!ast_strlen_zero(predial_string)) {
-               ast_replace_subargument_delimiter(predial_string);
-       }
-
        AST_LIST_LOCK(&dial->channels);
        AST_LIST_TRAVERSE(&dial->channels, channel, list) {
                if ((res = begin_dial_prerun(channel, chan, cap, predial_string))) {
@@ -450,10 +445,6 @@ static int begin_dial(struct ast_dial *dial, struct ast_channel *chan, int async
        int success = 0;
        char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-       if (!ast_strlen_zero(predial_string)) {
-               ast_replace_subargument_delimiter(predial_string);
-       }
-
        /* Iterate through channel list, requesting and calling each one */
        AST_LIST_LOCK(&dial->channels);
        AST_LIST_TRAVERSE(&dial->channels, channel, list) {
@@ -473,10 +464,6 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
        char *tech = "Local", *device = tmp, *stuff;
        char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-       if (!ast_strlen_zero(predial_string)) {
-               ast_replace_subargument_delimiter(predial_string);
-       }
-
        /* If call forwarding is disabled just drop the original channel and don't attempt to dial the new one */
        if (FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_DISABLE_CALL_FORWARDING)) {
                ast_hangup(original);
index df9d289..2132656 100644 (file)
@@ -464,7 +464,7 @@ static void endpoint_snapshot_dtor(void *obj)
 struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
        struct ast_endpoint *endpoint)
 {
-       RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
+       struct ast_endpoint_snapshot *snapshot;
        int channel_count;
        struct ao2_iterator i;
        void *obj;
@@ -500,7 +500,6 @@ struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
        }
        ao2_iterator_destroy(&i);
 
-       ao2_ref(snapshot, +1);
        return snapshot;
 }
 
index fdac22c..b02e08e 100644 (file)
@@ -278,6 +278,70 @@ static void make_components(struct logchannel *chan)
        chan->logmask = logmask;
 }
 
+/*!
+ * \brief create the filename that will be used for a logger channel.
+ *
+ * \param channel The name of the logger channel
+ * \param[out] filename The filename for the logger channel
+ * \param size The size of the filename buffer
+ */
+static void make_filename(const char *channel, char *filename, size_t size)
+{
+       const char *log_dir_prefix = "";
+       const char *log_dir_separator = "";
+
+       *filename = '\0';
+
+       if (!strcasecmp(channel, "console")) {
+               return;
+       }
+
+       if (!strncasecmp(channel, "syslog", 6)) {
+               ast_copy_string(filename, channel, size);
+               return;
+       }
+
+       /* It's a filename */
+
+       if (channel[0] != '/') {
+               log_dir_prefix = ast_config_AST_LOG_DIR;
+               log_dir_separator = "/";
+       }
+
+       if (!ast_strlen_zero(hostname)) {
+               snprintf(filename, size, "%s%s%s.%s",
+                       log_dir_prefix, log_dir_separator, channel, hostname);
+       } else {
+               snprintf(filename, size, "%s%s%s",
+                       log_dir_prefix, log_dir_separator, channel);
+       }
+}
+
+/*!
+ * \brief Find a particular logger channel by name
+ *
+ * \pre logchannels list is locked
+ *
+ * \param channel The name of the logger channel to find
+ * \retval non-NULL The corresponding logger channel
+ * \retval NULL Unable to find a logger channel with that particular name
+ */
+static struct logchannel *find_logchannel(const char *channel)
+{
+       char filename[PATH_MAX];
+       struct logchannel *chan;
+
+       make_filename(channel, filename, sizeof(filename));
+
+       AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+               if (!strcmp(chan->filename, filename)) {
+                       return chan;
+               }
+       }
+
+       return NULL;
+}
+
 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno, int dynamic)
 {
        struct logchannel *chan;
@@ -293,6 +357,8 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo
        chan->lineno = lineno;
        chan->dynamic = dynamic;
 
+       make_filename(channel, chan->filename, sizeof(chan->filename));
+
        if (!strcasecmp(channel, "console")) {
                chan->type = LOGTYPE_CONSOLE;
        } else if (!strncasecmp(channel, "syslog", 6)) {
@@ -314,25 +380,7 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo
                }
 
                chan->type = LOGTYPE_SYSLOG;
-               ast_copy_string(chan->filename, channel, sizeof(chan->filename));
-               openlog("asterisk", LOG_PID, chan->facility);
        } else {
-               const char *log_dir_prefix = "";
-               const char *log_dir_separator = "";
-
-               if (channel[0] != '/') {
-                       log_dir_prefix = ast_config_AST_LOG_DIR;
-                       log_dir_separator = "/";
-               }
-
-               if (!ast_strlen_zero(hostname)) {
-                       snprintf(chan->filename, sizeof(chan->filename), "%s%s%s.%s",
-                               log_dir_prefix, log_dir_separator, channel, hostname);
-               } else {
-                       snprintf(chan->filename, sizeof(chan->filename), "%s%s%s",
-                               log_dir_prefix, log_dir_separator, channel);
-               }
-
                if (!(chan->fileptr = fopen(chan->filename, "a"))) {
                        /* Can't do real logging here since we're called with a lock
                         * so log to any attached consoles */
@@ -929,13 +977,9 @@ int ast_logger_rotate_channel(const char *log_channel)
 {
        struct logchannel *f;
        int success = AST_LOGGER_FAILURE;
+       char filename[PATH_MAX];
 
-       struct ast_str *filename = ast_str_create(64);
-       if (!filename) {
-               return AST_LOGGER_ALLOC_ERROR;
-       }
-
-       ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel);
+       make_filename(log_channel, filename, sizeof(filename));
 
        AST_RWLIST_WRLOCK(&logchannels);
 
@@ -950,7 +994,7 @@ int ast_logger_rotate_channel(const char *log_channel)
                if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
                        fclose(f->fileptr);     /* Close file */
                        f->fileptr = NULL;
-                       if (strcmp(ast_str_buffer(filename), f->filename) == 0) {
+                       if (strcmp(filename, f->filename) == 0) {
                                rotate_file(f->filename);
                                success = AST_LOGGER_SUCCESS;
                        }
@@ -960,7 +1004,6 @@ int ast_logger_rotate_channel(const char *log_channel)
        init_logger_chain(1 /* locked */, NULL);
 
        AST_RWLIST_UNLOCK(&logchannels);
-       ast_free(filename);
 
        return success;
 }
@@ -1091,45 +1134,35 @@ static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struc
 int ast_logger_create_channel(const char *log_channel, const char *components)
 {
        struct logchannel *chan;
-       struct ast_str *filename = ast_str_create(64);
-       int chan_exists = AST_LOGGER_SUCCESS;
 
        if (ast_strlen_zero(components)) {
                return AST_LOGGER_DECLINE;
        }
 
-       if (!filename) {
-               return AST_LOGGER_ALLOC_ERROR;
-       }
-
-       ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel);
-
        AST_RWLIST_WRLOCK(&logchannels);
 
-       AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
-               if (!strcmp(ast_str_buffer(filename), chan->filename)) {
-                       chan_exists = AST_LOGGER_FAILURE;
-                       break;
-               }
+       chan = find_logchannel(log_channel);
+       if (chan) {
+               AST_RWLIST_UNLOCK(&logchannels);
+               return AST_LOGGER_FAILURE;
        }
 
-       if (!chan_exists) {
-               chan = make_logchannel(log_channel, components, 0, 1);
-               if (chan) {
-                       AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
-                       global_logmask |= chan->logmask;
-                       chan_exists = AST_LOGGER_SUCCESS;
-               }
+       chan = make_logchannel(log_channel, components, 0, 1);
+       if (!chan) {
+               AST_RWLIST_UNLOCK(&logchannels);
+               return AST_LOGGER_ALLOC_ERROR;
        }
+
+       AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
+       global_logmask |= chan->logmask;
+
        AST_RWLIST_UNLOCK(&logchannels);
 
-       return chan_exists;
+       return AST_LOGGER_SUCCESS;
 }
 
 static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct logchannel *chan;
-
        switch (cmd) {
        case CLI_INIT:
                e->command = "logger add channel";
@@ -1148,57 +1181,34 @@ static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct
                return CLI_SHOWUSAGE;
        }
 
-       AST_RWLIST_WRLOCK(&logchannels);
-       AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
-               if (!strcmp(chan->filename, a->argv[3])) {
-                       break;
-               }
-       }
-
-       if (chan) {
-               AST_RWLIST_UNLOCK(&logchannels);
-               ast_cli(a->fd, "Logger channel '%s' already exists\n", a->argv[3]);
+       switch (ast_logger_create_channel(a->argv[3], a->argv[4])) {
+       case AST_LOGGER_SUCCESS:
                return CLI_SUCCESS;
-       }
-
-       chan = make_logchannel(a->argv[3], a->argv[4], 0, 1);
-       if (chan) {
-               AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
-               global_logmask |= chan->logmask;
-               AST_RWLIST_UNLOCK(&logchannels);
+       case AST_LOGGER_FAILURE:
+               ast_cli(a->fd, "Logger channel '%s' already exists\n", a->argv[3]);
                return CLI_SUCCESS;
+       case AST_LOGGER_DECLINE:
+       case AST_LOGGER_ALLOC_ERROR:
+       default:
+               ast_cli(a->fd, "ERROR: Unable to create log channel '%s'\n", a->argv[3]);
+               return CLI_FAILURE;
        }
-
-       AST_RWLIST_UNLOCK(&logchannels);
-       ast_cli(a->fd, "ERROR: Unable to create log channel '%s'\n", a->argv[3]);
-
-       return CLI_FAILURE;
 }
 
 int ast_logger_remove_channel(const char *log_channel)
 {
        struct logchannel *chan;
-       struct ast_str *filename = ast_str_create(64);
-
-       if (!filename) {
-               return AST_LOGGER_ALLOC_ERROR;
-       }
-
-       ast_str_append(&filename, 0, "%s/%s", ast_config_AST_LOG_DIR, log_channel);
 
        AST_RWLIST_WRLOCK(&logchannels);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&logchannels, chan, list) {
-               if (chan->dynamic && !strcmp(chan->filename, ast_str_buffer(filename))) {
-                       AST_RWLIST_REMOVE_CURRENT(list);
-                       break;
-               }
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       AST_RWLIST_UNLOCK(&logchannels);
 
-       if (!chan) {
+       chan = find_logchannel(log_channel);
+       if (chan && chan->dynamic) {
+               AST_RWLIST_REMOVE(&logchannels, chan, list);
+       } else {
+               AST_RWLIST_UNLOCK(&logchannels);
                return AST_LOGGER_FAILURE;
        }
+       AST_RWLIST_UNLOCK(&logchannels);
 
        if (chan->fileptr) {
                fclose(chan->fileptr);
@@ -1246,30 +1256,17 @@ static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, stru
                return CLI_SHOWUSAGE;
        }
 
-       AST_RWLIST_WRLOCK(&logchannels);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&logchannels, chan, list) {
-               if (chan->dynamic && !strcmp(chan->filename, a->argv[3])) {
-                       AST_RWLIST_REMOVE_CURRENT(list);
-                       break;
-               }
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       AST_RWLIST_UNLOCK(&logchannels);
-
-       if (!chan) {
+       switch (ast_logger_remove_channel(a->argv[3])) {
+       case AST_LOGGER_SUCCESS:
+               ast_cli(a->fd, "Removed dynamic logger channel '%s'\n", a->argv[3]);
+               return CLI_SUCCESS;
+       case AST_LOGGER_FAILURE:
                ast_cli(a->fd, "Unable to find dynamic logger channel '%s'\n", a->argv[3]);
                return CLI_SUCCESS;
+       default:
+               ast_cli(a->fd, "Internal failure attempting to delete dynamic logger channel '%s'\n", a->argv[3]);
+               return CLI_FAILURE;
        }
-
-       ast_cli(a->fd, "Removed dynamic logger channel '%s'\n", chan->filename);
-       if (chan->fileptr) {
-               fclose(chan->fileptr);
-               chan->fileptr = NULL;
-       }
-       ast_free(chan);
-       chan = NULL;
-
-       return CLI_SUCCESS;
 }
 
 struct verb {
@@ -1299,7 +1296,7 @@ static struct sigaction handle_SIGXFSZ = {
        .sa_flags = SA_RESTART,
 };
 
-static void ast_log_vsyslog(struct logmsg *msg)
+static void ast_log_vsyslog(struct logmsg *msg, int facility)
 {
        char buf[BUFSIZ];
        int syslog_level = ast_syslog_priority_from_loglevel(msg->level);
@@ -1317,6 +1314,8 @@ static void ast_log_vsyslog(struct logmsg *msg)
                return;
        }
 
+       syslog_level = LOG_MAKEPRI(facility, syslog_level);
+
        snprintf(buf, sizeof(buf), "%s[%d]%s: %s:%d in %s: %s",
                 levels[msg->level], msg->lwp, call_identifier_str, msg->file, msg->line, msg->function, msg->message);
 
@@ -1402,7 +1401,7 @@ static void logger_print_normal(struct logmsg *logmsg)
 
                        /* Check syslog channels */
                        if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
-                               ast_log_vsyslog(logmsg);
+                               ast_log_vsyslog(logmsg, chan->facility);
                        /* Console channels */
                        } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
                                char linestr[128];
index 5e4f0a4..202940e 100644 (file)
@@ -5926,12 +5926,28 @@ static int ast_add_hint(struct ast_exten *e)
 /*! \brief Change hint for an extension */
 static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
 {
+       struct ast_str *hint_app;
        struct ast_hint *hint;
+       int previous_device_state;
+       char *previous_message = NULL;
+       char *message = NULL;
+       char *previous_subtype = NULL;
+       char *subtype = NULL;
+       int previous_presence_state;
+       int presence_state;
+       int presence_state_changed = 0;
 
        if (!oe || !ne) {
                return -1;
        }
 
+       hint_app = ast_str_create(1024);
+       if (!hint_app) {
+               return -1;
+       }
+
+       ast_mutex_lock(&context_merge_lock); /* Hold off ast_merge_contexts_and_delete and state changes */
+
        ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
 
        /*
@@ -5941,6 +5957,8 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        hint = ao2_find(hints, oe, OBJ_UNLINK);
        if (!hint) {
                ao2_unlock(hints);
+               ast_mutex_unlock(&context_merge_lock);
+               ast_free(hint_app);
                return -1;
        }
 
@@ -5949,7 +5967,28 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        /* Update the hint and put it back in the hints container. */
        ao2_lock(hint);
        hint->exten = ne;
+
+       /* Store the previous states so we know whether we need to notify state callbacks */
+       previous_device_state = hint->laststate;
+       previous_presence_state = hint->last_presence_state;
+       previous_message = hint->last_presence_message;
+       previous_subtype = hint->last_presence_subtype;
+
+       /* Update the saved device and presence state with the new extension */
+       hint->laststate = ast_extension_state2(ne, NULL);
+       hint->last_presence_state = AST_PRESENCE_INVALID;
+       hint->last_presence_subtype = NULL;
+       hint->last_presence_message = NULL;
+
+       presence_state = extension_presence_state_helper(ne, &subtype, &message);
+       if (presence_state > 0) {
+               hint->last_presence_state = presence_state;
+               hint->last_presence_subtype = subtype;
+               hint->last_presence_message = message;
+       }
+
        ao2_unlock(hint);
+
        ao2_link(hints, hint);
        if (add_hintdevice(hint, ast_get_extension_app(ne))) {
                ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
@@ -5958,8 +5997,98 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        }
 
        ao2_unlock(hints);
+
+       /* Locking for state callbacks is respected here and only the context_merge_lock lock is
+        * held during the state callback invocation. This will stop the normal state callback
+        * thread from being able to handle incoming state changes if they occur.
+        */
+
+       /* Determine if presence state has changed due to the change of the hint extension */
+       if ((hint->last_presence_state != previous_presence_state) ||
+               strcmp(S_OR(hint->last_presence_subtype, ""), S_OR(previous_subtype, "")) ||
+               strcmp(S_OR(hint->last_presence_message, ""), S_OR(previous_message, ""))) {
+               presence_state_changed = 1;
+       }
+
+       /* Notify any existing state callbacks if the device or presence state has changed */
+       if ((hint->laststate != previous_device_state) || presence_state_changed) {
+               struct ao2_iterator cb_iter;
+               struct ast_state_cb *state_cb;
+               struct ao2_container *device_state_info;
+               int first_extended_cb_call = 1;
+
+               /* For general callbacks */
+               cb_iter = ao2_iterator_init(statecbs, 0);
+               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+                       /* Unlike the normal state callbacks since something has explicitly provided us this extension
+                        * it will remain valid and unchanged for the lifetime of this function invocation.
+                        */
+                       if (hint->laststate != previous_device_state) {
+                               execute_state_callback(state_cb->change_cb,
+                                       ast_get_context_name(ast_get_extension_context(ne)),
+                                       ast_get_extension_name(ne),
+                                       state_cb->data,
+                                       AST_HINT_UPDATE_DEVICE,
+                                       hint,
+                                       NULL);
+                       }
+                       if (presence_state_changed) {
+                               execute_state_callback(state_cb->change_cb,
+                                       ast_get_context_name(ast_get_extension_context(ne)),
+                                       ast_get_extension_name(ne),
+                                       state_cb->data,
+                                       AST_HINT_UPDATE_PRESENCE,
+                                       hint,
+                                       NULL);
+                       }
+               }
+               ao2_iterator_destroy(&cb_iter);
+
+               ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(ne));
+
+               device_state_info = alloc_device_state_info();
+               ast_extension_state3(hint_app, device_state_info);
+
+               /* For extension callbacks */
+               cb_iter = ao2_iterator_init(hint->callbacks, 0);
+               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+                       if (hint->laststate != previous_device_state) {
+                               if (state_cb->extended && first_extended_cb_call) {
+                               /* Fill detailed device_state_info now that we know it is used by extd. callback */
+                                       first_extended_cb_call = 0;
+                                       get_device_state_causing_channels(device_state_info);
+                               }
+                               execute_state_callback(state_cb->change_cb,
+                                       ast_get_context_name(ast_get_extension_context(ne)),
+                                       ast_get_extension_name(ne),
+                                       state_cb->data,
+                                       AST_HINT_UPDATE_DEVICE,
+                                       hint,
+                                       state_cb->extended ? device_state_info : NULL);
+                       }
+                       if (presence_state_changed) {
+                               execute_state_callback(state_cb->change_cb,
+                                       ast_get_context_name(ast_get_extension_context(ne)),
+                                       ast_get_extension_name(ne),
+                                       state_cb->data,
+                                       AST_HINT_UPDATE_PRESENCE,
+                                       hint,
+                                       NULL);
+                       }
+               }
+               ao2_iterator_destroy(&cb_iter);
+
+               ao2_cleanup(device_state_info);
+       }
+
        ao2_ref(hint, -1);
 
+       ast_mutex_unlock(&context_merge_lock);
+
+       ast_free(hint_app);
+       ast_free(previous_message);
+       ast_free(previous_subtype);
+
        return 0;
 }
 
index 062b2fd..8f9e84b 100644 (file)
@@ -62,9 +62,26 @@ ASTERISK_REGISTER_FILE()
 
 AST_THREADSTORAGE(last_del_id);
 
+/*!
+ * \brief Scheduler ID holder
+ *
+ * These form a queue on a scheduler context. When a new
+ * scheduled item is created, a sched_id is popped off the
+ * queue and its id is assigned to the new scheduled item.
+ * When the scheduled task is complete, the sched_id on that
+ * task is then pushed to the back of the queue to be re-used
+ * on some future scheduled item.
+ */
+struct sched_id {
+       /*! Immutable ID number that is copied onto the scheduled task */
+       int id;
+       AST_LIST_ENTRY(sched_id) list;
+};
+
 struct sched {
        AST_LIST_ENTRY(sched) list;
-       int id;                       /*!< ID number of event */
+       /*! The ID that has been popped off the scheduler context's queue */
+       struct sched_id *sched_id;
        struct timeval when;          /*!< Absolute time event should take place */
        int resched;                  /*!< When to reschedule */
        int variable;                 /*!< Use return value from callback to reschedule */
@@ -99,6 +116,10 @@ struct ast_sched_context {
        AST_LIST_HEAD_NOLOCK(, sched) schedc;   /*!< Cache of unused schedule structures and how many */
        unsigned int schedccnt;
 #endif
+       /*! Queue of scheduler task IDs to assign */
+       AST_LIST_HEAD_NOLOCK(, sched_id) id_queue;
+       /*! The number of IDs in the id_queue */
+       int id_queue_size;
 };
 
 static void *sched_run(void *data)
@@ -208,6 +229,8 @@ struct ast_sched_context *ast_sched_context_create(void)
        ast_mutex_init(&tmp->lock);
        tmp->eventcnt = 1;
 
+       AST_LIST_HEAD_INIT_NOLOCK(&tmp->id_queue);
+
        if (!(tmp->sched_heap = ast_heap_create(8, sched_time_cmp,
                        offsetof(struct sched, __heap_index)))) {
                ast_sched_context_destroy(tmp);
@@ -219,6 +242,11 @@ struct ast_sched_context *ast_sched_context_create(void)
 
 static void sched_free(struct sched *task)
 {
+       /* task->sched_id will be NULL most of the time, but when the
+        * scheduler context shuts down, it will free all scheduled
+        * tasks, and in that case, the task->sched_id will be non-NULL
+        */
+       ast_free(task->sched_id);
        ast_cond_destroy(&task->cond);
        ast_free(task);
 }
@@ -226,6 +254,7 @@ static void sched_free(struct sched *task)
 void ast_sched_context_destroy(struct ast_sched_context *con)
 {
        struct sched *s;
+       struct sched_id *sid;
 
        sched_thread_destroy(con);
        con->sched_thread = NULL;
@@ -246,40 +275,75 @@ void ast_sched_context_destroy(struct ast_sched_context *con)
                con->sched_heap = NULL;
        }
 
+       while ((sid = AST_LIST_REMOVE_HEAD(&con->id_queue, list))) {
+               ast_free(sid);
+       }
+
        ast_mutex_unlock(&con->lock);
        ast_mutex_destroy(&con->lock);
 
        ast_free(con);
 }
 
-static struct sched *sched_alloc(struct ast_sched_context *con)
-{
-       struct sched *tmp;
+#define ID_QUEUE_INCREMENT 16
 
-       /*
-        * We keep a small cache of schedule entries
-        * to minimize the number of necessary malloc()'s
+/*!
+ * \brief Add new scheduler IDs to the queue.
+ *
+ * \retval The number of IDs added to the queue
+ */
+static int add_ids(struct ast_sched_context *con)
+{
+       int new_size;
+       int original_size;
+       int i;
+
+       original_size = con->id_queue_size;
+       /* So we don't go overboard with the mallocs here, we'll just up
+        * the size of the list by a fixed amount each time instead of
+        * multiplying the size by any particular factor
         */
-#ifdef SCHED_MAX_CACHE
-       if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list))) {
-               con->schedccnt--;
-       } else 
-#endif
-       {
-               tmp = ast_calloc(1, sizeof(*tmp));
-               ast_cond_init(&tmp->cond, NULL);
+       new_size = original_size + ID_QUEUE_INCREMENT;
+       if (new_size < 0) {
+               /* Overflow. Cap it at INT_MAX. */
+               new_size = INT_MAX;
        }
+       for (i = original_size; i < new_size; ++i) {
+               struct sched_id *new_id;
 
-       return tmp;
+               new_id = ast_calloc(1, sizeof(*new_id));
+               if (!new_id) {
+                       break;
+               }
+               new_id->id = i;
+               AST_LIST_INSERT_TAIL(&con->id_queue, new_id, list);
+               ++con->id_queue_size;
+       }
+
+       return con->id_queue_size - original_size;
+}
+
+static int set_sched_id(struct ast_sched_context *con, struct sched *new_sched)
+{
+       if (AST_LIST_EMPTY(&con->id_queue) && (add_ids(con) == 0)) {
+               return -1;
+       }
+
+       new_sched->sched_id = AST_LIST_REMOVE_HEAD(&con->id_queue, list);
+       return 0;
 }
 
 static void sched_release(struct ast_sched_context *con, struct sched *tmp)
 {
+       if (tmp->sched_id) {
+               AST_LIST_INSERT_TAIL(&con->id_queue, tmp->sched_id, list);
+               tmp->sched_id = NULL;
+       }
+
        /*
         * Add to the cache, or just free() if we
         * already have too many cache entries
         */
-
 #ifdef SCHED_MAX_CACHE
        if (con->schedccnt < SCHED_MAX_CACHE) {
                AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
@@ -289,6 +353,35 @@ static void sched_release(struct ast_sched_context *con, struct sched *tmp)
                sched_free(tmp);
 }
 
+static struct sched *sched_alloc(struct ast_sched_context *con)
+{
+       struct sched *tmp;
+
+       /*
+        * We keep a small cache of schedule entries
+        * to minimize the number of necessary malloc()'s
+        */
+#ifdef SCHED_MAX_CACHE
+       if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list))) {
+               con->schedccnt--;
+       } else
+#endif
+       {
+               tmp = ast_calloc(1, sizeof(*tmp));
+               if (!tmp) {
+                       return NULL;
+               }
+               ast_cond_init(&tmp->cond, NULL);
+       }
+
+       if (set_sched_id(con, tmp)) {
+               sched_release(con, tmp);
+               return NULL;
+       }
+
+       return tmp;
+}
+
 void ast_sched_clean_by_callback(struct ast_sched_context *con, ast_sched_cb match, ast_sched_cb cleanup_cb)
 {
        int i = 1;
@@ -388,7 +481,7 @@ int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb
 
        ast_mutex_lock(&con->lock);
        if ((tmp = sched_alloc(con))) {
-               tmp->id = con->eventcnt++;
+               con->eventcnt++;
                tmp->callback = callback;
                tmp->data = data;
                tmp->resched = when;
@@ -399,7 +492,7 @@ int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb
                        sched_release(con, tmp);
                } else {
                        schedule(con, tmp);
-                       res = tmp->id;
+                       res = tmp->sched_id->id;
                }
        }
 #ifdef DUMP_SCHEDULER
@@ -437,7 +530,7 @@ static struct sched *sched_find(struct ast_sched_context *con, int id)
        for (x = 1; x <= heap_size; x++) {
                struct sched *cur = ast_heap_peek(con->sched_heap, x);
 
-               if (cur->id == id) {
+               if (cur->sched_id->id == id) {
                        return cur;
                }
        }
@@ -488,16 +581,16 @@ int _ast_sched_del(struct ast_sched_context *con, int id, const char *file, int
        s = sched_find(con, id);
        if (s) {
                if (!ast_heap_remove(con->sched_heap, s)) {
-                       ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->id);
+                       ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->sched_id->id);
                }
                sched_release(con, s);
-       } else if (con->currently_executing && (id == con->currently_executing->id)) {
+       } else if (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
                s = con->currently_executing;
                s->deleted = 1;
                /* Wait for executing task to complete so that caller of ast_sched_del() does not
                 * free memory out from under the task.
                 */
-               while (con->currently_executing && (id == con->currently_executing->id)) {
+               while (con->currently_executing && (id == con->currently_executing->sched_id->id)) {
                        ast_cond_wait(&s->cond, &con->lock);
                }
                /* Do not sched_release() here because ast_sched_runq() will do it */
@@ -586,7 +679,7 @@ void ast_sched_dump(struct ast_sched_context *con)
                q = ast_heap_peek(con->sched_heap, x);
                delta = ast_tvsub(q->when, when);
                ast_debug(1, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
-                       q->id,
+                       q->sched_id->id,
                        q->callback,
                        q->data,
                        (long)delta.tv_sec,
index da65053..f44ce42 100644 (file)
@@ -124,12 +124,7 @@ struct stasis_topic *ast_endpoint_topic_all_cached(void)
        return stasis_cp_all_topic_cached(endpoint_cache_all);
 }
 
-static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *msg);
-
 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_snapshot_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_state_type,
-       .to_ami = peerstatus_to_ami,
-);
 
 static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *msg)
 {
@@ -166,10 +161,44 @@ static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *m
                ast_str_buffer(peerstatus_event_string));
 }
 
-static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg);
+static struct ast_json *peerstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
+{
+       struct ast_endpoint_blob *obj = stasis_message_data(msg);
+       struct ast_json *json_endpoint;
+       struct ast_json *json_peer;
+       struct ast_json *json_final;
+       const struct timeval *tv = stasis_message_timestamp(msg);
 
-STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
-       .to_ami = contactstatus_to_ami,
+       json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
+       if (!json_endpoint) {
+               return NULL;
+       }
+
+       json_peer = ast_json_object_create();
+       if (!json_peer) {
+               ast_json_unref(json_endpoint);
+               return NULL;
+       }
+
+       /* Copy all fields from the blob */
+       ast_json_object_update(json_peer, obj->blob);
+
+       json_final = ast_json_pack("{s: s, s: o, s: o, s: o }",
+               "type", "PeerStatusChange",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "endpoint", json_endpoint,
+               "peer", json_peer);
+       if (!json_final) {
+               ast_json_unref(json_endpoint);
+               ast_json_unref(json_peer);
+       }
+
+       return json_final;
+}
+
+STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_state_type,
+       .to_ami = peerstatus_to_ami,
+       .to_json = peerstatus_to_json,
 );
 
 static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg)
@@ -206,6 +235,39 @@ static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message
                "%s", ast_str_buffer(contactstatus_event_string));
 }
 
+static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
+{
+       struct ast_endpoint_blob *obj = stasis_message_data(msg);
+       struct ast_json *json_endpoint;
+       struct ast_json *json_final;
+       const struct timeval *tv = stasis_message_timestamp(msg);
+
+       json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
+       if (!json_endpoint) {
+               return NULL;
+       }
+
+       json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s } } ",
+               "type", "ContactStatusChange",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "endpoint", json_endpoint,
+               "contact_info",
+               "uri", ast_json_string_get(ast_json_object_get(obj->blob, "uri")),
+               "contact_status", ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")),
+               "aor", ast_json_string_get(ast_json_object_get(obj->blob, "aor")),
+               "roundtrip_usec", ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")));
+       if (!json_final) {
+               ast_json_unref(json_endpoint);
+       }
+
+       return json_final;
+}
+
+STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
+       .to_ami = contactstatus_to_ami,
+       .to_json = contactstatus_to_json
+);
+
 static void endpoint_blob_dtor(void *obj)
 {
        struct ast_endpoint_blob *event = obj;
index 4bcfa33..ba1a07c 100644 (file)
@@ -1251,8 +1251,8 @@ int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*st
                pthread_attr_init(attr);
        }
 
-#ifdef __linux__
-       /* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
+#if defined(__linux__) || defined(__FreeBSD__)
+       /* On Linux and FreeBSD , pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
           which is kind of useless. Change this here to
           PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
           priority will propagate down to new threads by default.
index 0a5c760..623d5b5 100644 (file)
@@ -4180,6 +4180,180 @@ ari_validator ast_ari_validate_channel_varset_fn(void)
        return ast_ari_validate_channel_varset;
 }
 
+int ast_ari_validate_contact_info(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_aor = 0;
+       int has_contact_status = 0;
+       int has_uri = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("aor", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_aor = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactInfo field aor failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("contact_status", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_contact_status = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactInfo field contact_status failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("roundtrip_usec", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactInfo field roundtrip_usec failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("uri", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_uri = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactInfo field uri failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ContactInfo has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_aor) {
+               ast_log(LOG_ERROR, "ARI ContactInfo missing required field aor\n");
+               res = 0;
+       }
+
+       if (!has_contact_status) {
+               ast_log(LOG_ERROR, "ARI ContactInfo missing required field contact_status\n");
+               res = 0;
+       }
+
+       if (!has_uri) {
+               ast_log(LOG_ERROR, "ARI ContactInfo missing required field uri\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+ari_validator ast_ari_validate_contact_info_fn(void)
+{
+       return ast_ari_validate_contact_info;
+}
+
+int ast_ari_validate_contact_status_change(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_type = 0;
+       int has_application = 0;
+       int has_contact_info = 0;
+       int has_endpoint = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactStatusChange field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactStatusChange field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactStatusChange field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("contact_info", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_contact_info = 1;
+                       prop_is_valid = ast_ari_validate_contact_info(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactStatusChange field contact_info failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("endpoint", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_endpoint = 1;
+                       prop_is_valid = ast_ari_validate_endpoint(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ContactStatusChange field endpoint failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ContactStatusChange has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_contact_info) {
+               ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field contact_info\n");
+               res = 0;
+       }
+
+       if (!has_endpoint) {
+               ast_log(LOG_ERROR, "ARI ContactStatusChange missing required field endpoint\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+ari_validator ast_ari_validate_contact_status_change_fn(void)
+{
+       return ast_ari_validate_contact_status_change;
+}
+
 int ast_ari_validate_device_state_changed(struct ast_json *json)
 {
        int res = 1;
@@ -4479,7 +4653,7 @@ int ast_ari_validate_event(struct ast_json *json)
 
        discriminator = ast_json_string_get(ast_json_object_get(json, "type"));
        if (!discriminator) {
-               ast_log(LOG_ERROR, "ARI Event missing required field type");
+               ast_log(LOG_ERROR, "ARI Event missing required field type\n");
                return 0;
        }
 
@@ -4552,6 +4726,9 @@ int ast_ari_validate_event(struct ast_json *json)
        if (strcmp("ChannelVarset", discriminator) == 0) {
                return ast_ari_validate_channel_varset(json);
        } else
+       if (strcmp("ContactStatusChange", discriminator) == 0) {
+               return ast_ari_validate_contact_status_change(json);
+       } else
        if (strcmp("DeviceStateChanged", discriminator) == 0) {
                return ast_ari_validate_device_state_changed(json);
        } else
@@ -4561,6 +4738,9 @@ int ast_ari_validate_event(struct ast_json *json)
        if (strcmp("EndpointStateChange", discriminator) == 0) {
                return ast_ari_validate_endpoint_state_change(json);
        } else
+       if (strcmp("PeerStatusChange", discriminator) == 0) {
+               return ast_ari_validate_peer_status_change(json);
+       } else
        if (strcmp("PlaybackFinished", discriminator) == 0) {
                return ast_ari_validate_playback_finished(json);
        } else
@@ -4656,7 +4836,7 @@ int ast_ari_validate_message(struct ast_json *json)
 
        discriminator = ast_json_string_get(ast_json_object_get(json, "type"));
        if (!discriminator) {
-               ast_log(LOG_ERROR, "ARI Message missing required field type");
+               ast_log(LOG_ERROR, "ARI Message missing required field type\n");
                return 0;
        }
 
@@ -4729,6 +4909,9 @@ int ast_ari_validate_message(struct ast_json *json)
        if (strcmp("ChannelVarset", discriminator) == 0) {
                return ast_ari_validate_channel_varset(json);
        } else
+       if (strcmp("ContactStatusChange", discriminator) == 0) {
+               return ast_ari_validate_contact_status_change(json);
+       } else
        if (strcmp("DeviceStateChanged", discriminator) == 0) {
                return ast_ari_validate_device_state_changed(json);
        } else
@@ -4744,6 +4927,9 @@ int ast_ari_validate_message(struct ast_json *json)
        if (strcmp("MissingParams", discriminator) == 0) {
                return ast_ari_validate_missing_params(json);
        } else
+       if (strcmp("PeerStatusChange", discriminator) == 0) {
+               return ast_ari_validate_peer_status_change(json);
+       } else
        if (strcmp("PlaybackFinished", discriminator) == 0) {
                return ast_ari_validate_playback_finished(json);
        } else
@@ -4861,6 +5047,175 @@ ari_validator ast_ari_validate_missing_params_fn(void)
        return ast_ari_validate_missing_params;
 }
 
+int ast_ari_validate_peer(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_peer_status = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("address", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Peer field address failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Peer field cause failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("peer_status", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_peer_status = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Peer field peer_status failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("port", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Peer field port failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("time", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Peer field time failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Peer has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_peer_status) {
+               ast_log(LOG_ERROR, "ARI Peer missing required field peer_status\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+ari_validator ast_ari_validate_peer_fn(void)
+{
+       return ast_ari_validate_peer;
+}
+
+int ast_ari_validate_peer_status_change(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_type = 0;
+       int has_application = 0;
+       int has_endpoint = 0;
+       int has_peer = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PeerStatusChange field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ast_ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PeerStatusChange field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PeerStatusChange field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("endpoint", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_endpoint = 1;
+                       prop_is_valid = ast_ari_validate_endpoint(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PeerStatusChange field endpoint failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("peer", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_peer = 1;
+                       prop_is_valid = ast_ari_validate_peer(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PeerStatusChange field peer failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI PeerStatusChange has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_endpoint) {
+               ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field endpoint\n");
+               res = 0;
+       }
+
+       if (!has_peer) {
+               ast_log(LOG_ERROR, "ARI PeerStatusChange missing required field peer\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+ari_validator ast_ari_validate_peer_status_change_fn(void)
+{
+       return ast_ari_validate_peer_status_change;
+}
+
 int ast_ari_validate_playback_finished(struct ast_json *json)
 {
        int res = 1;
index 1803f57..0bcdb0f 100644 (file)
@@ -1007,6 +1007,42 @@ int ast_ari_validate_channel_varset(struct ast_json *json);
 ari_validator ast_ari_validate_channel_varset_fn(void);
 
 /*!
+ * \brief Validator for ContactInfo.
+ *
+ * Detailed information about a contact on an endpoint.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_contact_info(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_contact_info().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_contact_info_fn(void);
+
+/*!
+ * \brief Validator for ContactStatusChange.
+ *
+ * The state of a contact on an endpoint has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_contact_status_change(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_contact_status_change().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_contact_status_change_fn(void);
+
+/*!
  * \brief Validator for DeviceStateChanged.
  *
  * Notification that a device state has changed.
@@ -1115,6 +1151,42 @@ int ast_ari_validate_missing_params(struct ast_json *json);
 ari_validator ast_ari_validate_missing_params_fn(void);
 
 /*!
+ * \brief Validator for Peer.
+ *
+ * Detailed information about a remote peer that communicates with Asterisk.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_peer(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_peer().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_peer_fn(void);
+
+/*!
+ * \brief Validator for PeerStatusChange.
+ *
+ * The state of a peer associated with an endpoint has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_peer_status_change(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_peer_status_change().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_peer_status_change_fn(void);
+
+/*!
  * \brief Validator for PlaybackFinished.
  *
  * Event showing the completion of a media playback operation.
@@ -1546,6 +1618,17 @@ ari_validator ast_ari_validate_application_fn(void);
  * - channel: Channel
  * - value: string (required)
  * - variable: string (required)
+ * ContactInfo
+ * - aor: string (required)
+ * - contact_status: string (required)
+ * - roundtrip_usec: string
+ * - uri: string (required)
+ * ContactStatusChange
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - contact_info: ContactInfo (required)
+ * - endpoint: Endpoint (required)
  * DeviceStateChanged
  * - type: string (required)
  * - application: string (required)
@@ -1575,6 +1658,18 @@ ari_validator ast_ari_validate_application_fn(void);
  * MissingParams
  * - type: string (required)
  * - params: List[string] (required)
+ * Peer
+ * - address: string
+ * - cause: string
+ * - peer_status: string (required)
+ * - port: string
+ * - time: string
+ * PeerStatusChange
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - endpoint: Endpoint (required)
+ * - peer: Peer (required)
  * PlaybackFinished
  * - type: string (required)
  * - application: string (required)
index deb7f9c..8fa15f5 100644 (file)
@@ -280,7 +280,9 @@ static void event_session_cleanup(struct event_session *session)
        }
 
        event_session_shutdown(session);
-       ao2_unlink(event_session_registry, session);
+       if (event_session_registry) {
+               ao2_unlink(event_session_registry, session);
+       }
 }
 
 /*!
@@ -367,6 +369,7 @@ static int event_session_alloc(struct ast_tcptls_session_instance *ser,
                struct ast_ari_events_event_websocket_args *args, const char *session_id)
 {
        RAII_VAR(struct event_session *, session, NULL, ao2_cleanup);
+       int (* register_handler)(const char *, stasis_app_cb handler, void *data);
        size_t size, i;
 
        /* The request must have at least one [app] parameter */
@@ -399,6 +402,12 @@ static int event_session_alloc(struct ast_tcptls_session_instance *ser,
        }
 
        /* Register the apps with Stasis */
+       if (args->subscribe_all) {
+               register_handler = &stasis_app_register_all;
+       } else {
+               register_handler = &stasis_app_register;
+       }
+
        for (i = 0; i < args->app_count; ++i) {
                const char *app = args->app[i];
 
@@ -411,10 +420,10 @@ static int event_session_alloc(struct ast_tcptls_session_instance *ser,
                        return event_session_allocation_error_handler(session, ERROR_TYPE_OOM, ser);
                }
 
-               if (stasis_app_register(app, stasis_app_message_handler, session)) {
+               if (register_handler(app, stasis_app_message_handler, session)) {
                        ast_log(LOG_WARNING, "Stasis registration failed for application: '%s'\n", app);
                        return event_session_allocation_error_handler(
-                               session, ERROR_TYPE_STASIS_REGISTRATION, ser);
+                               session, ERROR_TYPE_STASIS_REGISTRATION, ser);                  
                }
        }
 
@@ -426,8 +435,17 @@ static int event_session_alloc(struct ast_tcptls_session_instance *ser,
        return 0;
 }
 
+static int event_session_shutdown_cb(void *session, void *arg, int flags)
+{
+       event_session_cleanup(session);
+
+       return 0;
+}
+
 void ast_ari_websocket_events_event_websocket_dtor(void)
 {
+       ao2_callback(event_session_registry, OBJ_MULTIPLE | OBJ_NODATA, event_session_shutdown_cb, NULL);
+
        ao2_cleanup(event_session_registry);
        event_session_registry = NULL;
 }
@@ -462,7 +480,8 @@ void ast_ari_websocket_events_event_websocket_established(
                struct ast_ari_websocket_session *ws_session, struct ast_variable *headers,
                struct ast_ari_events_event_websocket_args *args)
 {
-       RAII_VAR(struct event_session *, session, NULL, event_session_cleanup);
+       struct event_session *session;
+
        struct ast_json *msg;
        const char *session_id;
 
@@ -474,7 +493,6 @@ void ast_ari_websocket_events_event_websocket_established(
 
        /* Find the event_session and update its websocket  */
        session = ao2_find(event_session_registry, session_id, OBJ_SEARCH_KEY);
-
        if (session) {
                ao2_unlink(event_session_registry, session);
                event_session_update_websocket(session, ws_session);
@@ -487,6 +505,9 @@ void ast_ari_websocket_events_event_websocket_established(
        while ((msg = ast_ari_websocket_session_read(ws_session))) {
                ast_json_unref(msg);
        }
+
+       event_session_cleanup(session);
+       ao2_ref(session, -1);
 }
 
 void ast_ari_events_user_event(struct ast_variable *headers,
index aa1e3df..8c03af4 100644 (file)
@@ -47,6 +47,8 @@ struct ast_ari_events_event_websocket_args {
        size_t app_count;
        /*! Parsing context for app. */
        char *app_parse;
+       /*! Subscribe to all Asterisk events. If provided, the applications listed will be subscribed to all events, effectively disabling the application specific subscriptions. Default is 'false'. */
+       int subscribe_all;
 };
 
 /*!
index 34b081b..0a0ea3c 100644 (file)
@@ -694,6 +694,68 @@ static struct park_announce_subscription_data *park_announce_subscription_data_c
        return pa_data;
 }
 
+/*! \internal
+ * \brief Gathers inheritable channel variables from a channel by name.
+ *
+ * \param oh outgoing helper struct we are bestowing inheritable variables to
+ * \param channel_id name or uniqueID of the channel to inherit variables from
+ *
+ * \return Nothing
+ */
+static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
+{
+       struct ast_channel *chan = ast_channel_get_by_name(channel_id);
+       struct ast_var_t *current;
+       struct ast_variable *newvar;
+       const char *varname;
+       int vartype;
+
+
+       if (!chan) {
+               /* Already gone */
+               return;
+       }
+
+       ast_channel_lock(chan);
+
+       AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
+               varname = ast_var_full_name(current);
+               if (!varname) {
+                       continue;
+               }
+
+               vartype = 0;
+               if (varname[0] == '_') {
+                       vartype = 1;
+                       if (varname[1] == '_') {
+                               vartype = 2;
+                       }
+               }
+
+               switch (vartype) {
+               case 1:
+                       newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
+                       break;
+               case 2:
+                       newvar = ast_variable_new(varname, ast_var_value(current), "");
+                       break;
+               default:
+                       continue;
+               }
+               if (newvar) {
+                       ast_debug(1, "Inheriting variable %s from %s.\n",
+                               newvar->name, ast_channel_name(chan));
+                       if (oh->vars) {
+                               newvar->next = oh->vars;
+                               oh->vars = newvar;
+                       }
+               }
+       }
+
+       ast_channel_unlock(chan);
+       ast_channel_cleanup(chan);
+}
+
 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
 {
        struct ast_channel *dchan;
@@ -715,6 +777,9 @@ static void announce_to_dial(char *dial_string, char *announce_string, int parki
 
        snprintf(buf, sizeof(buf), "%d", parkingspace);
        oh.vars = ast_variable_new("_PARKEDAT", buf, "");
+
+       inherit_channel_vars_from_id(&oh, parkee_snapshot->uniqueid);
+
        dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
                &outstate,
                parkee_snapshot->caller_number,
index 4b2b151..e4fda0a 100644 (file)
@@ -111,6 +111,9 @@ static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_sess
                                args.app[j] = (vals[j]);
                        }
                } else
+               if (strcmp(i->name, "subscribeAll") == 0) {
+                       args.subscribe_all = ast_true(i->value);
+               } else
                {}
        }
 
@@ -209,6 +212,9 @@ static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocke
                                args.app[j] = (vals[j]);
                        }
                } else
+               if (strcmp(i->name, "subscribeAll") == 0) {
+                       args.subscribe_all = ast_true(i->value);
+               } else
                {}
        }
 
index 5659b4e..4375c39 100644 (file)
@@ -781,8 +781,16 @@ static int cdr_handler(struct ast_cdr *cdr)
        struct ast_str *sql1 = ast_str_create(160), *sql2 = ast_str_create(16);
        int first = 1;
 
+       if (!sql1 || !sql2) {
+               ast_free(sql1);
+               ast_free(sql2);
+               return -1;
+       }
+
        if (!tbl) {
                ast_log(LOG_WARNING, "No such table: %s\n", cdr_table);
+               ast_free(sql1);
+               ast_free(sql2);
                return -1;
        }
 
index b8463ce..3e93b6f 100644 (file)
                                         Identifier names are usually derived from and can be found in the endpoint
                                         identifier module itself (res_pjsip_endpoint_identifier_*)</synopsis>
                                </configOption>
+                               <configOption name="default_from_user" default="asterisk">
+                                       <synopsis>When Asterisk generates an outgoing SIP request, the From header username will be
+                                        set to this value if there is no better option (such as CallerID) to be
+                                        used.</synopsis>
+                               </configOption>
                        </configObject>
                </configFile>
        </configInfo>
@@ -2333,10 +2338,11 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
        pjsip_sip_uri *sip_uri;
        pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
        int local_port;
-       char uuid_str[AST_UUID_STR_LEN];
+       char default_user[PJSIP_MAX_URL_SIZE];
 
        if (ast_strlen_zero(user)) {
-               user = ast_uuid_generate_str(uuid_str, sizeof(uuid_str));
+               ast_sip_get_default_from_user(default_user, sizeof(default_user));
+               user = default_user;
        }
 
        /* Parse the provided target URI so we can determine what transport it will end up using */
index 42ba234..ef706f0 100644 (file)
@@ -34,6 +34,7 @@
 #define DEFAULT_DEBUG "no"
 #define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
 #define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
+#define DEFAULT_FROM_USER "asterisk"
 
 static char default_useragent[256];
 
@@ -46,6 +47,8 @@ struct global_config {
                AST_STRING_FIELD(debug);
                /*! Order by which endpoint identifiers are checked (comma separated list) */
                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);
        );
        /* Value to put in Max-Forwards header */
        unsigned int max_forwards;
@@ -179,6 +182,19 @@ unsigned int ast_sip_get_max_initial_qualify_time(void)
        return time;
 }
 
+void ast_sip_get_default_from_user(char *from_user, size_t size)
+{
+       struct global_config *cfg;
+
+       cfg = get_global_cfg();
+       if (!cfg) {
+               ast_copy_string(from_user, DEFAULT_FROM_USER, size);
+       } else {
+               ast_copy_string(from_user, cfg->default_from_user, size);
+               ao2_ref(cfg, -1);
+       }
+}
+
 /*!
  * \internal
  * \brief Observer to set default global object if none exist.
@@ -292,6 +308,8 @@ int ast_sip_initialize_sorcery_global(void)
        ast_sorcery_object_field_register(sorcery, "global", "max_initial_qualify_time",
                __stringify(DEFAULT_MAX_INITIAL_QUALIFY_TIME),
                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));
 
        if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
                return -1;
index d87410d..9625f04 100644 (file)
@@ -47,6 +47,41 @@ static void *aor_alloc(const char *name)
        return aor;
 }
 
+/*! \brief Internal callback function which destroys the specified contact */
+static int destroy_contact(void *obj, void *arg, int flags)
+{
+       struct ast_sip_contact *contact = obj;
+
+       ast_sip_location_delete_contact(contact);
+
+       return CMP_MATCH;
+}
+
+static void aor_deleted_observer(const void *object)
+{
+       const char *aor_id = ast_sorcery_object_get_id(object);
+       /* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */
+       char regex[strlen(aor_id) + 4];
+       struct ao2_container *contacts;
+
+       snprintf(regex, sizeof(regex), "^%s;@", aor_id);
+
+       if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) {
+               return;
+       }
+
+       /* Destroy any contacts that may still exist that were made for this AoR */
+       ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
+
+       ao2_ref(contacts, -1);
+}
+
+/*! \brief Observer for contacts so state can be updated on respective endpoints */
+static const struct ast_sorcery_observer aor_observer = {
+       .deleted = aor_deleted_observer,
+};
+
+
 /*! \brief Destructor for contact */
 static void contact_destroy(void *obj)
 {
@@ -102,12 +137,6 @@ static int contact_link_static(void *obj, void *arg, int flags)
        return 0;
 }
 
-/*! \brief Simple callback function which returns immediately, used to grab the first contact of an AOR */
-static int contact_find_first(void *obj, void *arg, int flags)
-{
-       return CMP_MATCH | CMP_STOP;
-}
-
 struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
 {
        RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
@@ -118,7 +147,8 @@ struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct
                return NULL;
        }
 
-       contact = ao2_callback(contacts, 0, contact_find_first, NULL);
+       /* Get the first AOR contact in the container. */
+       contact = ao2_callback(contacts, 0, NULL, NULL);
        return contact;
 }
 
@@ -234,8 +264,10 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
 {
        char name[MAX_OBJECT_FIELD * 2 + 3];
        RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+       char hash[33];
 
-       snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri);
+       ast_md5_hash(hash, uri);
+       snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), hash);
 
        if (!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name))) {
                return -1;
@@ -910,6 +942,8 @@ int ast_sip_initialize_sorcery_location(void)
                return -1;
        }
 
+       ast_sorcery_observer_add(sorcery, "aor", &aor_observer);
+
        ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
        ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
        ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path));
@@ -971,6 +1005,7 @@ int ast_sip_initialize_sorcery_location(void)
 
 int ast_sip_destroy_sorcery_location(void)
 {
+       ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &aor_observer);
        ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
        ast_sip_unregister_cli_formatter(contact_formatter);
        ast_sip_unregister_cli_formatter(aor_formatter);
index 3dfae45..ea2c7cd 100644 (file)
@@ -117,7 +117,9 @@ static void set_redirecting_value(char **dst, const pj_str_t *src)
 {
        ast_free(*dst);
        *dst = ast_malloc(pj_strlen(src) + 1);
-       ast_copy_pj_str(*dst, src, pj_strlen(src) + 1);
+       if (*dst) {
+               ast_copy_pj_str(*dst, src, pj_strlen(src) + 1);
+       }
 }
 
 static void set_redirecting_id(pjsip_name_addr *name_addr, struct ast_party_id *data,
index 483caa2..683ae61 100644 (file)
@@ -56,7 +56,7 @@ static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg)
                                break;
                        }
                }
-       } else {
+       } else if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method)) {
                rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL);
        }
 
index 6c3d101..7d84b46 100644 (file)
@@ -982,6 +982,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
 
        tree->root = tree_node_alloc(resource, &visited, list->full_state);
        if (!tree->root) {
+               AST_VECTOR_FREE(&visited);
                return 500;
        }
 
@@ -1170,6 +1171,7 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
                return;
        }
 
+       /* We notify subscription shutdown only on the tree leaves. */
        if (sub->handler->subscription_shutdown) {
                sub->handler->subscription_shutdown(sub);
        }
@@ -1262,7 +1264,7 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
 
        sub_tree = allocate_subscription_tree(endpoint);
        if (!sub_tree) {
-               pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
+               *dlg_status = PJ_ENOMEM;
                return NULL;
        }
        sub_tree->role = AST_SIP_NOTIFIER;
@@ -1304,7 +1306,7 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
        return sub_tree;
 }
 
-static int generate_initial_notify(struct ast_sip_subscription *sub);
+static int initial_notify_task(void *obj);
 static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state);
 
 /*! \brief Callback function to perform the actual recreation of a subscription */
@@ -1358,7 +1360,7 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 
        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);
+       resource = ast_alloca(resource_size);
        ast_copy_pj_str(resource, &request_uri->user, resource_size);
 
        /* Update the expiration header with the new expiration */
@@ -1402,10 +1404,9 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
                }
                sub_tree->persistence = ao2_bump(persistence);
                subscription_persistence_update(sub_tree, &rdata);
-               if (generate_initial_notify(sub_tree->root)) {
+               if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ao2_bump(sub_tree))) {
                        pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
-               } else {
-                       send_notify(sub_tree, 1);
+                       ao2_ref(sub_tree, -1);
                }
        } else {
                ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
@@ -1526,6 +1527,7 @@ void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, co
        return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
 }
 
+/* XXX This function is not used. */
 struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
                struct ast_sip_endpoint *endpoint, const char *resource)
 {
@@ -2522,6 +2524,7 @@ static int generate_initial_notify(struct ast_sip_subscription *sub)
                return 0;
        }
 
+       /* We notify subscription establishment only on the tree leaves. */
        if (sub->handler->notifier->subscription_established(sub)) {
                return -1;
        }
@@ -2541,6 +2544,24 @@ static int generate_initial_notify(struct ast_sip_subscription *sub)
        return res;
 }
 
+static int initial_notify_task(void * obj)
+{
+       struct sip_subscription_tree *sub_tree;
+
+       sub_tree = obj;
+       if (generate_initial_notify(sub_tree->root)) {
+               pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+       } else {
+               send_notify(sub_tree, 1);
+               ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED",
+                       "Resource: %s",
+                       sub_tree->root->resource);
+       }
+
+       ao2_ref(sub_tree, -1);
+       return 0;
+}
+
 static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 {
        pjsip_expires_hdr *expires_header;
@@ -2578,7 +2599,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 
        request_uri_sip = pjsip_uri_get_uri(request_uri);
        resource_size = pj_strlen(&request_uri_sip->user) + 1;
-       resource = alloca(resource_size);
+       resource = ast_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);
@@ -2628,13 +2649,9 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
                sub_tree->persistence = subscription_persistence_create(sub_tree);
                subscription_persistence_update(sub_tree, rdata);
                sip_subscription_accept(sub_tree, rdata, resp);
-               if (generate_initial_notify(sub_tree->root)) {
+               if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ao2_bump(sub_tree))) {
                        pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
-               } else {
-                       send_notify(sub_tree, 1);
-                       ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED",
-                                       "Resource: %s",
-                                       sub_tree->root->resource);
+                       ao2_ref(sub_tree, -1);
                }
        }
 
@@ -2795,7 +2812,7 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
 
        request_uri_sip = pjsip_uri_get_uri(request_uri);
        resource_size = pj_strlen(&request_uri_sip->user) + 1;
-       resource_name = alloca(resource_size);
+       resource_name = ast_alloca(resource_size);
        ast_copy_pj_str(resource_name, &request_uri_sip->user, resource_size);
 
        resource = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "inbound-publication", resource_name);
@@ -2995,16 +3012,13 @@ int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator
        AST_LIST_INSERT_HEAD(&body_generators, generator, list);
        AST_RWLIST_UNLOCK(&body_generators);
 
-       /* Lengths of type and subtype plus space for a slash. pj_str_t is not
-        * null-terminated, so there is no need to allocate for the extra null
-        * byte
-        */
+       /* Lengths of type and subtype plus a slash. */
        accept_len = strlen(generator->type) + strlen(generator->subtype) + 1;
 
-       accept.ptr = alloca(accept_len);
-       accept.slen = accept_len;
-       /* Safe use of sprintf */
-       sprintf(accept.ptr, "%s/%s", generator->type, generator->subtype);
+       /* Add room for null terminator that sprintf() will set. */
+       pj_strset(&accept, ast_alloca(accept_len + 1), accept_len);
+       sprintf((char *) pj_strbuf(&accept), "%s/%s", generator->type, generator->subtype);/* Safe */
+
        pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
                        PJSIP_H_ACCEPT, NULL, 1, &accept);
 
@@ -4270,6 +4284,7 @@ static int load_module(void)
        if (apply_list_configuration(sorcery)) {
                ast_sip_unregister_service(&pubsub_module);
                ast_sched_context_destroy(sched);
+               return AST_MODULE_LOAD_FAILURE;
        }
 
        ast_sorcery_apply_default(sorcery, "inbound-publication", "config", "pjsip.conf,criteria=type=inbound-publication");
index 1dcac7e..fc87248 100644 (file)
@@ -1297,6 +1297,11 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
        session->contact = ao2_bump(contact);
        session->inv_session = inv_session;
        session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!session->req_caps) {
+               /* Release the ref held by session->inv_session */
+               ao2_ref(session, -1);
+               return NULL;
+       }
 
        if ((endpoint->dtmf == AST_SIP_DTMF_INBAND) || (endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
                dsp_features |= DSP_FEATURE_DIGIT_DETECT;
index e48b630..3157a94 100644 (file)
@@ -419,7 +419,7 @@ static int load_module(void)
        CHECK_PJSIP_MODULE_LOADED();
 
        pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WS", 5060, &transport_type_ws);
-       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WSS", 5060, &transport_type_wss);
+       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "WSS", 5060, &transport_type_wss);
 
        if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
                return AST_MODULE_LOAD_DECLINE;
index 545e216..a51e07c 100644 (file)
@@ -590,12 +590,16 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
        pj_ice_sess_role role = rtp->ice->role;
        int res;
 
+       ast_debug(3, "Resetting ICE for RTP instance '%p'\n", instance);
        if (!rtp->ice->is_nominating && !rtp->ice->is_complete) {
+               ast_debug(3, "Nevermind. ICE isn't ready for a reset\n");
                return 0;
        }
 
+       ast_debug(3, "Stopping ICE for RTP instance '%p'\n", instance);
        ast_rtp_ice_stop(instance);
 
+       ast_debug(3, "Recreating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->ice_original_rtp_addr), rtp->ice_port, instance);
        res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
        if (!res) {
                /* Preserve the role that the old ICE session used */
@@ -648,6 +652,7 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
        /* Check for equivalence in the lists */
        if (rtp->ice_active_remote_candidates &&
                        !ice_candidates_compare(rtp->ice_proposed_remote_candidates, rtp->ice_active_remote_candidates)) {
+               ast_debug(3, "Proposed == active candidates for RTP instance '%p'\n", instance);
                ao2_cleanup(rtp->ice_proposed_remote_candidates);
                rtp->ice_proposed_remote_candidates = NULL;
                return;
@@ -694,8 +699,10 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
                }
 
                if (candidate->id == AST_RTP_ICE_COMPONENT_RTP && rtp->turn_rtp) {
+                       ast_debug(3, "RTP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
                        pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1);
                } else if (candidate->id == AST_RTP_ICE_COMPONENT_RTCP && rtp->turn_rtcp) {
+                       ast_debug(3, "RTCP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
                        pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1);
                }
 
@@ -705,21 +712,40 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 
        ao2_iterator_destroy(&i);
 
-       if (has_rtp && has_rtcp &&
-           pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(
-                                                 rtp->ice_active_remote_candidates), &candidates[0]) == PJ_SUCCESS) {
-               ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
-               pj_ice_sess_start_check(rtp->ice);
-               pj_timer_heap_poll(timer_heap, NULL);
-               rtp->strict_rtp_state = STRICT_RTP_OPEN;
-               return;
+       if (cand_cnt < ao2_container_count(rtp->ice_active_remote_candidates)) {
+               ast_log(LOG_WARNING, "Lost %d ICE candidates. Consider increasing PJ_ICE_MAX_CAND in PJSIP (%p)\n",
+                       ao2_container_count(rtp->ice_active_remote_candidates) - cand_cnt, instance);
+       }
+
+       if (!has_rtp) {
+               ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance);
+       }
+
+       if (!has_rtcp) {
+               ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance);
+       }
+
+       if (has_rtp && has_rtcp) {
+               pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
+               char reason[80];
+
+               if (res == PJ_SUCCESS) {
+                       ast_debug(3, "Successfully created ICE checklist (%p)\n", instance);
+                       ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
+                       pj_ice_sess_start_check(rtp->ice);
+                       pj_timer_heap_poll(timer_heap, NULL);
+                       rtp->strict_rtp_state = STRICT_RTP_OPEN;
+                       return;
+               }
+
+               pj_strerror(res, reason, sizeof(reason));
+               ast_log(LOG_WARNING, "Failed to create ICE session check list: %s (%p)\n", reason, instance);
        }
 
        ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: FAILURE");
 
        /* even though create check list failed don't stop ice as
           it might still work */
-       ast_debug(1, "Failed to create ICE session check list\n");
        /* however we do need to reset remote candidates since
           this function may be re-entered */
        ao2_ref(rtp->ice_active_remote_candidates, -1);
@@ -769,7 +795,11 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+       ast_debug(3, "Set role to %s (%p)\n",
+               role == AST_RTP_ICE_ROLE_CONTROLLED ? "CONTROLLED" : "CONTROLLING", instance);
+
        if (!rtp->ice) {
+               ast_log(LOG_WARNING, "Set role failed; no ice instance (%p)\n", instance);
                return;
        }
 
@@ -2485,7 +2515,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
             create_new_socket("RTP",
                               ast_sockaddr_is_ipv4(addr) ? AF_INET  :
                               ast_sockaddr_is_ipv6(addr) ? AF_INET6 : -1)) < 0) {
-               ast_debug(1, "Failed to create a new socket for RTP instance '%p'\n", instance);
+               ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance);
                ast_free(rtp);
                return -1;
        }
@@ -2526,6 +2556,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 #ifdef HAVE_PJPROJECT
        /* Create an ICE session for ICE negotiation */
        if (icesupport) {
+               ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
                if (ice_create(instance, addr, x, 0)) {
                        ast_log(LOG_NOTICE, "Failed to start ICE session\n");
                } else {
index f7d8299..69e9b93 100644 (file)
@@ -109,6 +109,11 @@ struct ao2_container *app_bridges_moh;
 
 struct ao2_container *app_bridges_playback;
 
+/*!
+ * \internal \brief List of registered event sources.
+ */
+AST_RWLIST_HEAD_STATIC(event_sources, stasis_app_event_source);
+
 static struct ast_json *stasis_end_to_json(struct stasis_message *message,
                const struct stasis_message_sanitizer *sanitize)
 {
@@ -1469,7 +1474,7 @@ struct ao2_container *stasis_app_get_all(void)
        return ao2_bump(apps);
 }
 
-int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
+static int __stasis_app_register(const char *app_name, stasis_app_cb handler, void *data, int all_events)
 {
        RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
 
@@ -1482,8 +1487,20 @@ int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
        if (app) {
                app_update(app, handler, data);
        } else {
-               app = app_create(app_name, handler, data);
+               app = app_create(app_name, handler, data, all_events ? STASIS_APP_SUBSCRIBE_ALL : STASIS_APP_SUBSCRIBE_MANUAL);
                if (app) {
+                       if (all_events) {
+                               struct stasis_app_event_source *source;
+                               SCOPED_LOCK(lock, &event_sources, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+
+                               AST_LIST_TRAVERSE(&event_sources, source, next) {
+                                       if (!source->subscribe) {
+                                               continue;
+                                       }
+
+                                       source->subscribe(app, NULL);
+                               }
+                       }
                        ao2_link_flags(apps_registry, app, OBJ_NOLOCK);
                } else {
                        ao2_unlock(apps_registry);
@@ -1499,6 +1516,16 @@ int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
        return 0;
 }
 
+int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
+{
+       return __stasis_app_register(app_name, handler, data, 0);
+}
+
+int stasis_app_register_all(const char *app_name, stasis_app_cb handler, void *data)
+{
+       return __stasis_app_register(app_name, handler, data, 1);
+}
+
 void stasis_app_unregister(const char *app_name)
 {
        RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
@@ -1526,11 +1553,6 @@ void stasis_app_unregister(const char *app_name)
        cleanup();
 }
 
-/*!
- * \internal \brief List of registered event sources.
- */
-AST_RWLIST_HEAD_STATIC(event_sources, stasis_app_event_source);
-
 void stasis_app_register_event_source(struct stasis_app_event_source *obj)
 {
        SCOPED_LOCK(lock, &event_sources, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
@@ -1727,8 +1749,8 @@ static enum stasis_app_subscribe_res app_subscribe(
 
        ast_debug(3, "%s: Checking %s\n", app_name, uri);
 
-       if (!event_source->find ||
-           (!(obj = event_source->find(app, uri + strlen(event_source->scheme))))) {
+       if (!ast_strlen_zero(uri + strlen(event_source->scheme)) &&
+           (!event_source->find || (!(obj = event_source->find(app, uri + strlen(event_source->scheme)))))) {
                ast_log(LOG_WARNING, "Event source not found: %s\n", uri);
                return STASIS_ASR_EVENT_SOURCE_NOT_FOUND;
        }
@@ -2062,6 +2084,7 @@ static int load_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application support",
+       .load_pri = AST_MODPRI_APP_DEPEND,
        .support_level = AST_MODULE_SUPPORT_CORE,
        .load = load_module,
        .unload = unload_module,
index 7f7c513..4093646 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_REGISTER_FILE()
 /*! Number of hash buckets for device state subscriptions */
 #define DEVICE_STATE_BUCKETS 37
 
+/*! The key used for tracking a subscription to all device states */
+#define DEVICE_STATE_ALL "__AST_DEVICE_STATE_ALL_TOPIC"
+
 /*! Container for subscribed device states */
 static struct ao2_container *device_state_subscriptions;
 
@@ -112,11 +115,17 @@ static void device_state_subscription_destroy(void *obj)
 static struct device_state_subscription *device_state_subscription_create(
        const struct stasis_app *app, const char *device_name)
 {
-       struct device_state_subscription *sub = ao2_alloc(
-               sizeof(*sub), device_state_subscription_destroy);
+       struct device_state_subscription *sub;
        const char *app_name = stasis_app_name(app);
-       size_t size = strlen(device_name) + strlen(app_name) + 2;
+       size_t size;
+
+       if (ast_strlen_zero(device_name)) {
+               device_name = DEVICE_STATE_ALL;
+       }
 
+       size = strlen(device_name) + strlen(app_name) + 2;
+
+       sub = ao2_alloc(sizeof(*sub), device_state_subscription_destroy);
        if (!sub) {
                return NULL;
        }
@@ -314,25 +323,50 @@ static void *find_device_state(const struct stasis_app *app, const char *name)
 
 static int is_subscribed_device_state(struct stasis_app *app, const char *name)
 {
-       RAII_VAR(struct device_state_subscription *, sub,
-                find_device_state_subscription(app, name), ao2_cleanup);
-       return sub != NULL;
+       struct device_state_subscription *sub;
+
+       sub = find_device_state_subscription(app, DEVICE_STATE_ALL);
+       if (sub) {
+               ao2_ref(sub, -1);
+               return 1;
+       }
+
+       sub = find_device_state_subscription(app, name);
+       if (sub) {
+               ao2_ref(sub, -1);
+               return 1;
+       }
+
+       return 0;
 }
 
 static int subscribe_device_state(struct stasis_app *app, void *obj)
 {
        struct device_state_subscription *sub = obj;
+       struct stasis_topic *topic;
+
+       if (!sub) {
+               sub = device_state_subscription_create(app, NULL);
+               if (!sub) {
+                       return -1;
+               }
+       }
 
-       ast_debug(3, "Subscribing to device %s", sub->device_name);
+       if (strcmp(sub->device_name, DEVICE_STATE_ALL)) {
+               topic = ast_device_state_topic(sub->device_name);
+       } else {
+               topic = ast_device_state_topic_all();
+       }
 
        if (is_subscribed_device_state(app, sub->device_name)) {
                ast_debug(3, "App %s is already subscribed to %s\n", stasis_app_name(app), sub->device_name);
                return 0;
        }
 
-       if (!(sub->sub = stasis_subscribe_pool(
-                       ast_device_state_topic(sub->device_name),
-                       device_state_cb, sub))) {
+       ast_debug(3, "Subscribing to device %s\n", sub->device_name);
+
+       sub->sub = stasis_subscribe_pool(topic, device_state_cb, sub);
+       if (!sub->sub) {
                ast_log(LOG_ERROR, "Unable to subscribe to device %s\n",
                        sub->device_name);
                return -1;
index b99e232..5002a0b 100644 (file)
@@ -38,6 +38,10 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/stasis_endpoints.h"
 #include "asterisk/stasis_message_router.h"
 
+#define BRIDGE_ALL "__AST_BRIDGE_ALL_TOPIC"
+#define CHANNEL_ALL "__AST_CHANNEL_ALL_TOPIC"
+#define ENDPOINT_ALL "__AST_ENDPOINT_ALL_TOPIC"
+
 static int unsubscribe(struct stasis_app *app, const char *kind, const char *id, int terminate);
 
 struct stasis_app {
@@ -47,12 +51,16 @@ struct stasis_app {
        struct stasis_message_router *router;
        /*! Router for handling messages to the bridge all \a topic. */
        struct stasis_message_router *bridge_router;
+       /*! Optional router for handling endpoint messages in 'all' subscriptions */
+       struct stasis_message_router *endpoint_router;
        /*! Container of the channel forwards to this app's topic. */
        struct ao2_container *forwards;
        /*! Callback function for this application. */
        stasis_app_cb handler;
        /*! Opaque data to hand to callback function. */
        void *data;
+       /*! Subscription model for the application */
+       enum stasis_app_subscription_model subscription_model;
        /*! Name of the Stasis application */
        char name[];
 };
@@ -121,34 +129,33 @@ static struct app_forwards *forwards_create(struct stasis_app *app,
 static struct app_forwards *forwards_create_channel(struct stasis_app *app,
        struct ast_channel *chan)
 {
-       RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+       struct app_forwards *forwards;
 
-       if (!app || !chan) {
+       if (!app) {
                return NULL;
        }
 
-       forwards = forwards_create(app, ast_channel_uniqueid(chan));
+       forwards = forwards_create(app, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL);
        if (!forwards) {
                return NULL;
        }
 
        forwards->forward_type = FORWARD_CHANNEL;
-       forwards->topic_forward = stasis_forward_all(ast_channel_topic(chan),
-               app->topic);
-       if (!forwards->topic_forward) {
-               return NULL;
+       if (chan) {
+               forwards->topic_forward = stasis_forward_all(ast_channel_topic(chan),
+                       app->topic);
        }
-
        forwards->topic_cached_forward = stasis_forward_all(
-               ast_channel_topic_cached(chan), app->topic);
-       if (!forwards->topic_cached_forward) {
+               chan ? ast_channel_topic_cached(chan) : ast_channel_topic_all_cached(),
+               app->topic);
+
+       if ((!forwards->topic_forward && chan) || !forwards->topic_cached_forward) {
                /* Half-subscribed is a bad thing */
-               stasis_forward_cancel(forwards->topic_forward);
-               forwards->topic_forward = NULL;
+               forwards_unsubscribe(forwards);
+               ao2_ref(forwards, -1);
                return NULL;
        }
 
-       ao2_ref(forwards, +1);
        return forwards;
 }
 
@@ -156,69 +163,100 @@ static struct app_forwards *forwards_create_channel(struct stasis_app *app,
 static struct app_forwards *forwards_create_bridge(struct stasis_app *app,
        struct ast_bridge *bridge)
 {
-       RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+       struct app_forwards *forwards;
 
-       if (!app || !bridge) {
+       if (!app) {
                return NULL;
        }
 
-       forwards = forwards_create(app, bridge->uniqueid);
+       forwards = forwards_create(app, bridge ? bridge->uniqueid : BRIDGE_ALL);
        if (!forwards) {
                return NULL;
        }
 
        forwards->forward_type = FORWARD_BRIDGE;
-       forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge),
-               app->topic);
-       if (!forwards->topic_forward) {
-               return NULL;
+       if (bridge) {
+               forwards->topic_forward = stasis_forward_all(ast_bridge_topic(bridge),
+                       app->topic);
        }
-
        forwards->topic_cached_forward = stasis_forward_all(
-               ast_bridge_topic_cached(bridge), app->topic);
-       if (!forwards->topic_cached_forward) {
+               bridge ? ast_bridge_topic_cached(bridge) : ast_bridge_topic_all_cached(),
+               app->topic);
+
+       if ((!forwards->topic_forward && bridge) || !forwards->topic_cached_forward) {
                /* Half-subscribed is a bad thing */
-               stasis_forward_cancel(forwards->topic_forward);
-               forwards->topic_forward = NULL;
+               forwards_unsubscribe(forwards);
+               ao2_ref(forwards, -1);
                return NULL;
        }
 
-       ao2_ref(forwards, +1);
        return forwards;
 }
 
+static void endpoint_state_cb(void *data, struct stasis_subscription *sub,
+       struct stasis_message *message)
+{
+       struct stasis_app *app = data;
+
+       stasis_publish(app->topic, message);
+}
+
 /*! Forward a endpoint's topics to an app */
 static struct app_forwards *forwards_create_endpoint(struct stasis_app *app,
        struct ast_endpoint *endpoint)
 {
-       RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+       struct app_forwards *forwards;
+       int ret = 0;
 
-       if (!app || !endpoint) {
+       if (!app) {
                return NULL;
        }
 
-       forwards = forwards_create(app, ast_endpoint_get_id(endpoint));
+       forwards = forwards_create(app, endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL);
        if (!forwards) {
                return NULL;
        }
 
        forwards->forward_type = FORWARD_ENDPOINT;
-       forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint),
-               app->topic);
-       if (!forwards->topic_forward) {
-               return NULL;
-       }
+       if (endpoint) {
+               forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint),
+                       app->topic);
+               forwards->topic_cached_forward = stasis_forward_all(
+                       ast_endpoint_topic_cached(endpoint), app->topic);
+
+               if (!forwards->topic_forward || !forwards->topic_cached_forward) {
+                       /* Half-subscribed is a bad thing */
+                       forwards_unsubscribe(forwards);
+                       ao2_ref(forwards, -1);
+                       return NULL;
+               }
+       } else {
+               /* Since endpoint subscriptions also subscribe to channels, in the case
+                * of all endpoint subscriptions, we only want messages for the endpoints.
+                * As such, we route those particular messages and then re-publish them
+                * on the app's topic.
+                */
+               ast_assert(app->endpoint_router == NULL);
+               app->endpoint_router = stasis_message_router_create(ast_endpoint_topic_all_cached());
+               if (!app->endpoint_router) {
+                       forwards_unsubscribe(forwards);
+                       ao2_ref(forwards, -1);
+                       return NULL;
+               }
 
-       forwards->topic_cached_forward = stasis_forward_all(
-               ast_endpoint_topic_cached(endpoint), app->topic);
-       if (!forwards->topic_cached_forward) {
-               /* Half-subscribed is a bad thing */
-               stasis_forward_cancel(forwards->topic_forward);
-               forwards->topic_forward = NULL;
-               return NULL;
+               ret |= stasis_message_router_add(app->endpoint_router,
+                       ast_endpoint_state_type(), endpoint_state_cb, app);
+               ret |= stasis_message_router_add(app->endpoint_router,
+                       ast_endpoint_contact_state_type(), endpoint_state_cb, app);
+
+               if (ret) {
+                       ao2_ref(app->endpoint_router, -1);
+                       app->endpoint_router = NULL;
+                       ao2_ref(forwards, -1);
+                       return NULL;
+               }
        }
 
-       ao2_ref(forwards, +1);
        return forwards;
 }
 
@@ -260,6 +298,7 @@ static void app_dtor(void *obj)
 
        ast_assert(app->router == NULL);
        ast_assert(app->bridge_router == NULL);
+       ast_assert(app->endpoint_router == NULL);
 
        ao2_cleanup(app->topic);
        app->topic = NULL;
@@ -793,7 +832,7 @@ static void bridge_default_handler(void *data, struct stasis_subscription *sub,
        }
 }
 
-struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data)
+struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model)
 {
        RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
        size_t size;
@@ -806,10 +845,10 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
 
        size = sizeof(*app) + strlen(name) + 1;
        app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX);
-
        if (!app) {
                return NULL;
        }
+       app->subscription_model = subscription_model;
 
        app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
                AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT,
@@ -877,7 +916,8 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
        return app;
 }
 
-struct stasis_topic *ast_app_get_topic(struct stasis_app *app) {
+struct stasis_topic *ast_app_get_topic(struct stasis_app *app)
+{
        return app->topic;
 }
 
@@ -930,6 +970,8 @@ void app_shutdown(struct stasis_app *app)
        app->router = NULL;
        stasis_message_router_unsubscribe(app->bridge_router);
        app->bridge_router = NULL;
+       stasis_message_router_unsubscribe(app->endpoint_router);
+       app->endpoint_router = NULL;
 }
 
 int app_is_active(struct stasis_app *app)
@@ -1029,34 +1071,47 @@ struct ast_json *app_to_json(const struct stasis_app *app)
 
 int app_subscribe_channel(struct stasis_app *app, struct ast_channel *chan)
 {
+       struct app_forwards *forwards;
+       SCOPED_AO2LOCK(lock, app->forwards);
        int res;
 
-       if (!app || !chan) {
+       if (!app) {
                return -1;
-       } else {
-               RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-               SCOPED_AO2LOCK(lock, app->forwards);
+       }
 
-               forwards = ao2_find(app->forwards, ast_channel_uniqueid(chan),
-                       OBJ_SEARCH_KEY | OBJ_NOLOCK);
-               if (!forwards) {
-                       /* Forwards not found, create one */
-                       forwards = forwards_create_channel(app, chan);
-                       if (!forwards) {
-                               return -1;
-                       }
+       /* If subscribed to all, don't subscribe again */
+       forwards = ao2_find(app->forwards, CHANNEL_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (forwards) {
+               ao2_ref(forwards, -1);
+               return 0;
+       }
 
-                       res = ao2_link_flags(app->forwards, forwards,
-                               OBJ_NOLOCK);
-                       if (!res) {
-                               return -1;
-                       }
+       forwards = ao2_find(app->forwards,
+               chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL,
+               OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (!forwards) {
+               /* Forwards not found, create one */
+               forwards = forwards_create_channel(app, chan);
+               if (!forwards) {
+                       return -1;
                }
 
-               ++forwards->interested;
-               ast_debug(3, "Channel '%s' is %d interested in %s\n", ast_channel_uniqueid(chan), forwards->interested, app->name);
-               return 0;
+               res = ao2_link_flags(app->forwards, forwards,
+                       OBJ_NOLOCK);
+               if (!res) {
+                       ao2_ref(forwards, -1);
+                       return -1;
+               }
        }
+
+       ++forwards->interested;
+       ast_debug(3, "Channel '%s' is %d interested in %s\n",
+               chan ? ast_channel_uniqueid(chan) : "ALL",
+               forwards->interested,
+               app->name);
+
+       ao2_ref(forwards, -1);
+       return 0;
 }
 
 static int subscribe_channel(struct stasis_app *app, void *obj)
@@ -1069,6 +1124,19 @@ static int unsubscribe(struct stasis_app *app, const char *kind, const char *id,
        RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
        SCOPED_AO2LOCK(lock, app->forwards);
 
+       if (!id) {
+               if (!strcmp(kind, "bridge")) {
+                       id = BRIDGE_ALL;
+               } else if (!strcmp(kind, "channel")) {
+                       id = CHANNEL_ALL;
+               } else if (!strcmp(kind, "endpoint")) {
+                       id = ENDPOINT_ALL;
+               } else {
+                       ast_log(LOG_WARNING, "Unknown subscription kind '%s'\n", kind);
+                       return -1;
+               }
+       }
+
        forwards = ao2_find(app->forwards, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
        if (!forwards) {
                ast_debug(3, "App '%s' not subscribed to %s '%s'\n", app->name, kind, id);
@@ -1095,16 +1163,16 @@ static int unsubscribe(struct stasis_app *app, const char *kind, const char *id,
 
 int app_unsubscribe_channel(struct stasis_app *app, struct ast_channel *chan)
 {
-       if (!app || !chan) {
+       if (!app) {
                return -1;
        }
 
-       return app_unsubscribe_channel_id(app, ast_channel_uniqueid(chan));
+       return app_unsubscribe_channel_id(app, chan ? ast_channel_uniqueid(chan) : CHANNEL_ALL);
 }
 
 int app_unsubscribe_channel_id(struct stasis_app *app, const char *channel_id)
 {
-       if (!app || !channel_id) {
+       if (!app) {
                return -1;
        }
 
@@ -1114,6 +1182,10 @@ int app_unsubscribe_channel_id(struct stasis_app *app, const char *channel_id)
 int app_is_subscribed_channel_id(struct stasis_app *app, const char *channel_id)
 {
        RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+
+       if (ast_strlen_zero(channel_id)) {
+               channel_id = CHANNEL_ALL;
+       }
        forwards = ao2_find(app->forwards, channel_id, OBJ_SEARCH_KEY);
        return forwards != NULL;
 }
@@ -1133,28 +1205,39 @@ struct stasis_app_event_source channel_event_source = {
 
 int app_subscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
 {
-       if (!app || !bridge) {
+       struct app_forwards *forwards;
+       SCOPED_AO2LOCK(lock, app->forwards);
+
+       if (!app) {
                return -1;
-       } else {
-               RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-               SCOPED_AO2LOCK(lock, app->forwards);
+       }
 
-               forwards = ao2_find(app->forwards, bridge->uniqueid,
-                       OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       /* If subscribed to all, don't subscribe again */
+       forwards = ao2_find(app->forwards, BRIDGE_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (forwards) {
+               ao2_ref(forwards, -1);
+               return 0;
+       }
 
+       forwards = ao2_find(app->forwards, bridge ? bridge->uniqueid : BRIDGE_ALL,
+               OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (!forwards) {
+               /* Forwards not found, create one */
+               forwards = forwards_create_bridge(app, bridge);
                if (!forwards) {
-                       /* Forwards not found, create one */
-                       forwards = forwards_create_bridge(app, bridge);
-                       if (!forwards) {
-                               return -1;
-                       }
-                       ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
+                       return -1;
                }
-
-               ++forwards->interested;
-               ast_debug(3, "Bridge '%s' is %d interested in %s\n", bridge->uniqueid, forwards->interested, app->name);
-               return 0;
+               ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
        }
+
+       ++forwards->interested;
+       ast_debug(3, "Bridge '%s' is %d interested in %s\n",
+               bridge ? bridge->uniqueid : "ALL",
+               forwards->interested,
+               app->name);
+
+       ao2_ref(forwards, -1);
+       return 0;
 }
 
 static int subscribe_bridge(struct stasis_app *app, void *obj)
@@ -1164,16 +1247,16 @@ static int subscribe_bridge(struct stasis_app *app, void *obj)
 
 int app_unsubscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
 {
-       if (!app || !bridge) {
+       if (!app) {
                return -1;
        }
 
-       return app_unsubscribe_bridge_id(app, bridge->uniqueid);
+       return app_unsubscribe_bridge_id(app, bridge ? bridge->uniqueid : BRIDGE_ALL);
 }
 
 int app_unsubscribe_bridge_id(struct stasis_app *app, const char *bridge_id)
 {
-       if (!app || !bridge_id) {
+       if (!app) {
                return -1;
        }
 
@@ -1182,9 +1265,26 @@ int app_unsubscribe_bridge_id(struct stasis_app *app, const char *bridge_id)
 
 int app_is_subscribed_bridge_id(struct stasis_app *app, const char *bridge_id)
 {
-       RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-       forwards = ao2_find(app->forwards, bridge_id, OBJ_SEARCH_KEY);
-       return forwards != NULL;
+       struct app_forwards *forwards;
+       SCOPED_AO2LOCK(lock, app->forwards);
+
+       forwards = ao2_find(app->forwards, BRIDGE_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (forwards) {
+               ao2_ref(forwards, -1);
+               return 1;
+       }
+
+       if (ast_strlen_zero(bridge_id)) {
+               bridge_id = BRIDGE_ALL;
+       }
+
+       forwards = ao2_find(app->forwards, bridge_id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (forwards) {
+               ao2_ref(forwards, -1);
+               return 1;
+       }
+
+       return 0;
 }
 
 static void *bridge_find(const struct stasis_app *app, const char *id)
@@ -1202,31 +1302,43 @@ struct stasis_app_event_source bridge_event_source = {
 
 int app_subscribe_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint)
 {
-       if (!app || !endpoint) {
+       struct app_forwards *forwards;
+       SCOPED_AO2LOCK(lock, app->forwards);
+
+       if (!app) {
                return -1;
-       } else {
-               RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
-               SCOPED_AO2LOCK(lock, app->forwards);
+       }
 
-               forwards = ao2_find(app->forwards, ast_endpoint_get_id(endpoint),
-                       OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       /* If subscribed to all, don't subscribe again */
+       forwards = ao2_find(app->forwards, ENDPOINT_ALL, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (forwards) {
+               ao2_ref(forwards, -1);
+               return 0;
+       }
 
+       forwards = ao2_find(app->forwards,
+               endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL,
+               OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (!forwards) {
+               /* Forwards not found, create one */
+               forwards = forwards_create_endpoint(app, endpoint);
                if (!forwards) {
-                       /* Forwards not found, create one */
-                       forwards = forwards_create_endpoint(app, endpoint);
-                       if (!forwards) {
-                               return -1;
-                       }
-                       ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
-
-                       /* Subscribe for messages */
-                       messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app);
+                       return -1;
                }
+               ao2_link_flags(app->forwards, forwards, OBJ_NOLOCK);
 
-               ++forwards->interested;
-               ast_debug(3, "Endpoint '%s' is %d interested in %s\n", ast_endpoint_get_id(endpoint), forwards->interested, app->name);
-               return 0;
+               /* Subscribe for messages */
+               messaging_app_subscribe_endpoint(app->name, endpoint, &message_received_handler, app);
        }
+
+       ++forwards->interested;
+       ast_debug(3, "Endpoint '%s' is %d interested in %s\n",
+               endpoint ? ast_endpoint_get_id(endpoint) : "ALL",
+               forwards->interested,
+               app->name);
+
+       ao2_ref(forwards, -1);
+       return 0;
 }
 
 static int subscribe_endpoint(struct stasis_app *app, void *obj)
@@ -1236,7 +1348,7 @@ static int subscribe_endpoint(struct stasis_app *app, void *obj)
 
 int app_unsubscribe_endpoint_id(struct stasis_app *app, const char *endpoint_id)
 {
-       if (!app || !endpoint_id) {
+       if (!app) {
                return -1;
        }
 
@@ -1246,6 +1358,10 @@ int app_unsubscribe_endpoint_id(struct stasis_app *app, const char *endpoint_id)
 int app_is_subscribed_endpoint_id(struct stasis_app *app, const char *endpoint_id)
 {
        RAII_VAR(struct app_forwards *, forwards, NULL, ao2_cleanup);
+
+       if (ast_strlen_zero(endpoint_id)) {
+               endpoint_id = ENDPOINT_ALL;
+       }
        forwards = ao2_find(app->forwards, endpoint_id, OBJ_SEARCH_KEY);
        return forwards != NULL;
 }
index 59574f5..2c8db1c 100644 (file)
  */
 struct stasis_app;
 
+enum stasis_app_subscription_model {
+       /*
+        * \brief An application must manually subscribe to each
+        * resource that it cares about. This is the default approach.
+        */
+       STASIS_APP_SUBSCRIBE_MANUAL,
+       /*
+        * \brief An application is automatically subscribed to all
+        * resources in Asterisk, even if it does not control them.
+        */
+       STASIS_APP_SUBSCRIBE_ALL
+};
+
 /*!
  * \brief Create a res_stasis application.
  *
@@ -45,7 +58,7 @@ struct stasis_app;
  * \return New \c res_stasis application.
  * \return \c NULL on error.
  */
-struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data);
+struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model);
 
 /*!
  * \brief Tears down an application.
index bf8aebb..16e167e 100644 (file)
@@ -38,6 +38,11 @@ ASTERISK_REGISTER_FILE()
 #include "messaging.h"
 
 /*!
+ * \brief Subscription to all technologies
+ */
+#define TECH_WILDCARD "__AST_ALL_TECH"
+
+/*!
  * \brief Number of buckets for the \ref endpoint_subscriptions container
  */
 #define ENDPOINTS_NUM_BUCKETS 127
@@ -219,10 +224,14 @@ static int has_destination_cb(const struct ast_msg *msg)
        for (i = 0; i < AST_VECTOR_SIZE(&tech_subscriptions); i++) {
                sub = AST_VECTOR_GET(&tech_subscriptions, i);
 
-               if (sub && (!strncasecmp(sub->token, buf, strlen(sub->token))
-                           || !strncasecmp(sub->token, buf, strlen(sub->token)))) {
+               if (!sub) {
+                       continue;
+               }
+
+               if (!strcmp(sub->token, TECH_WILDCARD)
+                   || !strncasecmp(sub->token, buf, strlen(sub->token))
+                   || !strncasecmp(sub->token, buf, strlen(sub->token))) {
                        ast_rwlock_unlock(&tech_subscriptions_lock);
-                       sub = NULL; /* No ref bump! */
                        goto match;
                }
 
@@ -231,6 +240,7 @@ static int has_destination_cb(const struct ast_msg *msg)
 
        sub = ao2_find(endpoint_subscriptions, buf, OBJ_SEARCH_KEY);
        if (sub) {
+               ao2_ref(sub, -1);
                goto match;
        }
 
@@ -238,7 +248,6 @@ static int has_destination_cb(const struct ast_msg *msg)
        return 0;
 
 match:
-       ao2_cleanup(sub);
        return 1;
 }
 
@@ -301,7 +310,8 @@ static int handle_msg_cb(struct ast_msg *msg)
                        continue;
                }
 
-               if (!strncasecmp(sub->token, buf, strlen(sub->token))) {
+               if (!strcmp(sub->token, TECH_WILDCARD)
+                   || !strncasecmp(sub->token, buf, strlen(sub->token))) {
                        ast_rwlock_unlock(&tech_subscriptions_lock);
                        ao2_bump(sub);
                        endpoint_name = buf;
@@ -374,7 +384,7 @@ static struct message_subscription *get_subscription(struct ast_endpoint *endpoi
 {
        struct message_subscription *sub = NULL;
 
-       if (!ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
+       if (endpoint && !ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
                sub = ao2_find(endpoint_subscriptions, endpoint, OBJ_SEARCH_KEY);
        } else {
                int i;
@@ -383,7 +393,7 @@ static struct message_subscription *get_subscription(struct ast_endpoint *endpoi
                for (i = 0; i < AST_VECTOR_SIZE(&tech_subscriptions); i++) {
                        sub = AST_VECTOR_GET(&tech_subscriptions, i);
 
-                       if (sub && !strcmp(sub->token, ast_endpoint_get_tech(endpoint))) {
+                       if (sub && !strcmp(sub->token, endpoint ? ast_endpoint_get_tech(endpoint) : TECH_WILDCARD)) {
                                ao2_bump(sub);
                                break;
                        }
@@ -400,10 +410,6 @@ void messaging_app_unsubscribe_endpoint(const char *app_name, const char *endpoi
        RAII_VAR(struct ast_endpoint *, endpoint, NULL, ao2_cleanup);
 
        endpoint = ast_endpoint_find_by_id(endpoint_id);
-       if (!endpoint) {
-               return;
-       }
-
        sub = get_subscription(endpoint);
        if (!sub) {
                return;
@@ -417,11 +423,11 @@ void messaging_app_unsubscribe_endpoint(const char *app_name, const char *endpoi
 
        AST_VECTOR_REMOVE_CMP_UNORDERED(&sub->applications, app_name, application_tuple_cmp, ao2_cleanup);
        if (AST_VECTOR_SIZE(&sub->applications) == 0) {
-               if (!ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
+               if (endpoint && !ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
                        ao2_unlink(endpoint_subscriptions, sub);
                } else {
                        ast_rwlock_wrlock(&tech_subscriptions_lock);
-                       AST_VECTOR_REMOVE_CMP_UNORDERED(&tech_subscriptions, ast_endpoint_get_id(endpoint),
+                       AST_VECTOR_REMOVE_CMP_UNORDERED(&tech_subscriptions, endpoint ? ast_endpoint_get_id(endpoint) : TECH_WILDCARD,
                                messaging_subscription_cmp, AST_VECTOR_ELEM_CLEANUP_NOOP);
                        ast_rwlock_unlock(&tech_subscriptions_lock);
                }
@@ -429,9 +435,9 @@ void messaging_app_unsubscribe_endpoint(const char *app_name, const char *endpoi
        ao2_unlock(sub);
        ao2_ref(sub, -1);
 
-       ast_debug(3, "App '%s' unsubscribed to messages from endpoint '%s'\n", app_name, ast_endpoint_get_id(endpoint));
+       ast_debug(3, "App '%s' unsubscribed to messages from endpoint '%s'\n", app_name, endpoint ? ast_endpoint_get_id(endpoint) : "-- ALL --");
        ast_test_suite_event_notify("StasisMessagingSubscription", "SubState: Unsubscribed\r\nAppName: %s\r\nToken: %s\r\n",
-               app_name, ast_endpoint_get_id(endpoint));
+               app_name, endpoint ? ast_endpoint_get_id(endpoint) : "ALL");
 }
 
 static struct message_subscription *get_or_create_subscription(struct ast_endpoint *endpoint)
@@ -442,12 +448,12 @@ static struct message_subscription *get_or_create_subscription(struct ast_endpoi
                return sub;
        }
 
-       sub = message_subscription_alloc(ast_endpoint_get_id(endpoint));
+       sub = message_subscription_alloc(endpoint ? ast_endpoint_get_id(endpoint) : TECH_WILDCARD);
        if (!sub) {
                return NULL;
        }
 
-       if (!ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
+       if (endpoint && !ast_strlen_zero(ast_endpoint_get_resource(endpoint))) {
                ao2_link(endpoint_subscriptions, sub);
        } else {
                ast_rwlock_wrlock(&tech_subscriptions_lock);
@@ -482,9 +488,9 @@ int messaging_app_subscribe_endpoint(const char *app_name, struct ast_endpoint *
        AST_VECTOR_APPEND(&sub->applications, tuple);
        ao2_unlock(sub);
 
-       ast_debug(3, "App '%s' subscribed to messages from endpoint '%s'\n", app_name, ast_endpoint_get_id(endpoint));
+       ast_debug(3, "App '%s' subscribed to messages from endpoint '%s'\n", app_name, endpoint ? ast_endpoint_get_id(endpoint) : "-- ALL --");
        ast_test_suite_event_notify("StasisMessagingSubscription", "SubState: Subscribed\r\nAppName: %s\r\nToken: %s\r\n",
-               app_name, ast_endpoint_get_id(endpoint));
+               app_name, endpoint ? ast_endpoint_get_id(endpoint) : "ALL");
 
        return 0;
 }
index 0ca3d26..78f19bf 100644 (file)
@@ -50,7 +50,7 @@ int ast_ari_validate_{{c_id}}(struct ast_json *json)
 
        discriminator = ast_json_string_get(ast_json_object_get(json, "{{discriminator.name}}"));
        if (!discriminator) {
-               ast_log(LOG_ERROR, "ARI {{id}} missing required field {{discriminator.name}}");
+               ast_log(LOG_ERROR, "ARI {{id}} missing required field {{discriminator.name}}\n");
                return 0;
        }
 
index 392b0ac..dee7c2d 100644 (file)
                                                        "required": true,
                                                        "allowMultiple": true,
                                                        "dataType": "string"
+                                               },
+                                               {
+                                                       "name": "subscribeAll",
+                                                       "description": "Subscribe to all Asterisk events. If provided, the applications listed will be subscribed to all events, effectively disabling the application specific subscriptions. Default is 'false'.",
+                                                       "paramType": "query",
+                                                       "required": false,
+                                                       "allowMultiple": false,
+                                                       "dataType": "boolean"
                                                }
                                        ]
                                }
                                "ChannelTalkingFinished",
                                "ChannelHold",
                                "ChannelUnhold",
+                               "ContactStatusChange",
                                "EndpointStateChange",
                                "Dial",
                                "StasisEnd",
                                "StasisStart",
                                "TextMessageReceived",
-                               "ChannelConnectedLine"
+                               "ChannelConnectedLine",
+                               "PeerStatusChange"
                        ]
                },
+               "ContactInfo": {
+                       "id": "ContactInfo",
+                       "description": "Detailed information about a contact on an endpoint.",
+                       "properties": {
+                               "uri": {
+                                       "type": "string",
+                                       "description": "The location of the contact.",
+                                       "required": true
+                               },
+                               "contact_status": {
+                                       "type": "string",
+                                       "description": "The current status of the contact.",
+                                       "required": true,
+                                       "allowableValues": {
+                                               "valueType": "LIST",
+                                               "values": [
+                                                       "Unreachable",
+                                                       "Reachable",
+                                                       "Unknown",
+                                                       "Created",
+                                                       "Removed"
+                                               ]
+                                       }
+                               },
+                               "aor": {
+                                       "type": "string",
+                                       "description": "The Address of Record this contact belongs to.",
+                                       "required": true
+                               },
+                               "roundtrip_usec": {
+                                       "type": "string",
+                                       "description": "Current round trip time, in microseconds, for the contact.",
+                                       "required": false
+                               }
+                       }
+               },
+               "Peer": {
+                       "id": "Peer",
+                       "description": "Detailed information about a remote peer that communicates with Asterisk.",
+                       "properties": {
+                               "peer_status": {
+                                       "type": "string",
+                                       "description": "The current state of the peer. Note that the values of the status are dependent on the underlying peer technology.",
+                                       "required": true
+                               },
+                               "cause": {
+                                       "type": "string",
+                                       "description": "An optional reason associated with the change in peer_status.",
+                                       "required": false
+                               },
+                               "address": {
+                                       "type": "string",
+                                       "description": "The IP address of the peer.",
+                                       "required": false
+                               },
+                               "port": {
+                                       "type": "string",
+                                       "description": "The port of the peer.",
+                                       "required": false
+                               },
+                               "time": {
+                                       "type": "string",
+                                       "description": "The last known time the peer was contacted.",
+                                       "required": false
+                               }
+                       }
+               },
                "DeviceStateChanged": {
                        "id": "DeviceStateChanged",
                        "description": "Notification that a device state has changed.",
                                }
                        }
                },
+               "ContactStatusChange": {
+                       "id": "ContactStatusChange",
+                       "description": "The state of a contact on an endpoint has changed.",
+                       "properties": {
+                               "endpoint": {
+                                       "required": true,
+                                       "type": "Endpoint"
+                               },
+                               "contact_info": {
+                                       "required": true,
+                                       "type": "ContactInfo"
+                               }
+                       }
+               },
+               "PeerStatusChange": {
+                       "id": "PeerStatusChange",
+                       "description": "The state of a peer associated with an endpoint has changed.",
+                       "properties": {
+                               "endpoint": {
+                                       "required": true,
+                                       "type": "Endpoint"
+                               },
+                               "peer": {
+                                       "required": true,
+                                       "type": "Peer"
+                               }
+                       }
+               },
                "EndpointStateChange": {
                        "id": "EndpointStateChange",
                        "description": "Endpoint state changed.",