loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / pbx.c
index eb395eb..b5602b5 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_REGISTER_FILE()
-
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"    /* use ast_config_AST_SYSTEM_NAME */
 #include <ctype.h>
@@ -237,9 +235,11 @@ AST_THREADSTORAGE(extensionstate_buf);
        priority.
 */
 struct ast_exten {
-       char *exten;                    /*!< Extension name */
+       char *exten;                    /*!< Clean Extension id */
+       char *name;                     /*!< Extension name (may include '-' eye candy) */
        int matchcid;                   /*!< Match caller id ? */
        const char *cidmatch;           /*!< Caller id to match for this extension */
+       const char *cidmatch_display;   /*!< Caller id to match (display version) */
        int priority;                   /*!< Priority */
        const char *label;              /*!< Label */
        struct ast_context *parent;     /*!< The context this extension belongs to  */
@@ -251,20 +251,12 @@ struct ast_exten {
        struct ast_hashtab *peer_table;    /*!< Priorities list in hashtab form -- only on the head of the peer list */
        struct ast_hashtab *peer_label_table; /*!< labeled priorities in the peers -- only on the head of the peer list */
        const char *registrar;          /*!< Registrar */
+       const char *registrar_file;     /*!< File name used to register extension */
+       int registrar_line;             /*!< Line number the extension was registered in text */
        struct ast_exten *next;         /*!< Extension with a greater ID */
        char stuff[0];
 };
 
-/*! \brief ast_sw: Switch statement in extensions.conf */
-struct ast_sw {
-       char *name;
-       const char *registrar;                  /*!< Registrar */
-       char *data;                             /*!< Data load */
-       int eval;
-       AST_LIST_ENTRY(ast_sw) list;
-       char stuff[0];
-};
-
 /*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
 struct match_char
 {
@@ -297,10 +289,10 @@ struct ast_context {
        struct ast_context *next;               /*!< Link them together */
        struct ast_includes includes;           /*!< Include other contexts */
        struct ast_ignorepats ignorepats;       /*!< Patterns for which to continue playing dialtone */
+       struct ast_sws alts;                    /*!< Alternative switches */
        char *registrar;                        /*!< Registrar -- make sure you malloc this, as the registrar may have to survive module unloads */
        int refcount;                   /*!< each module that would have created this context should inc/dec this as appropriate */
        int autohints;                  /*!< Whether autohints support is enabled or not */
-       AST_LIST_HEAD_NOLOCK(, ast_sw) alts;    /*!< Alternative switches */
        ast_mutex_t macrolock;                  /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
        char name[0];                           /*!< Name of the context */
 };
@@ -660,10 +652,12 @@ static int ast_add_extension_nolock(const char *context, int replace, const char
 static int ast_add_extension2_lockopt(struct ast_context *con,
        int replace, const char *extension, int priority, const char *label, const char *callerid,
        const char *application, void *data, void (*datad)(void *),
-       const char *registrar, int lock_context);
+       const char *registrar, const char *registrar_file, int registrar_line,
+       int lock_context);
 static struct ast_context *find_context_locked(const char *context);
 static struct ast_context *find_context(const char *context);
 static void get_device_state_causing_channels(struct ao2_container *c);
+static unsigned int ext_strncpy(char *dst, const char *src, size_t dst_size, int nofluff);
 
 /*!
  * \internal
@@ -888,9 +882,13 @@ int check_contexts(char *file, int line )
                                e2 = ast_hashtab_lookup(c1->root_table, &ex);
                                if (!e2) {
                                        if (e1->matchcid == AST_EXT_MATCHCID_ON) {
-                                               ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s (CID match: %s) but it is not in its root_table\n", file, line, c2->name, dummy_name, e1->cidmatch );
+                                               ast_log(LOG_NOTICE, "Called from: %s:%d: The %s context records "
+                                                       "the exten %s (CID match: %s) but it is not in its root_table\n",
+                                                       file, line, c2->name, dummy_name, e1->cidmatch_display);
                                        } else {
-                                               ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, dummy_name );
+                                               ast_log(LOG_NOTICE, "Called from: %s:%d: The %s context records "
+                                                       "the exten %s but it is not in its root_table\n",
+                                                       file, line, c2->name, dummy_name);
                                        }
                                        check_contexts_trouble();
                                }
@@ -1123,11 +1121,11 @@ static void cli_match_char_tree(struct match_char *node, char *prefix, int fd)
        if (strlen(node->x) > 1) {
                ast_cli(fd, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
                        node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
-                       node->exten ? node->exten->exten : "", extenstr);
+                       node->exten ? node->exten->name : "", extenstr);
        } else {
                ast_cli(fd, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
                        node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
-                       node->exten ? node->exten->exten : "", extenstr);
+                       node->exten ? node->exten->name : "", extenstr);
        }
 
        ast_str_set(&my_prefix, 0, "%s+       ", prefix);
@@ -1242,7 +1240,7 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
                                                                                return;                                                                                          \
                                                                        }                                                                                                    \
                                                                } else {                                                                                                 \
-                                                                       ast_debug(4, "returning an exact match-- first found-- %s\n", p->exten->exten);                       \
+                                                                       ast_debug(4, "returning an exact match-- first found-- %s\n", p->exten->name);                       \
                                                                        return; /* the first match, by definition, will be the best, because of the sorted tree */           \
                                                                }                                                                                                        \
                                                        }                                                                                                            \
@@ -1255,13 +1253,13 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
                                                if (*(str + 1) || p->next_char->x[0] == '!') {                                                         \
                                                        new_find_extension(str + 1, score, p->next_char, length + 1, spec + p->specificity, callerid, label, action); \
                                                        if (score->exten)  {                                                                             \
-                                                       ast_debug(4 ,"returning an exact match-- %s\n", score->exten->exten);                         \
+                                                       ast_debug(4 ,"returning an exact match-- %s\n", score->exten->name);                         \
                                                                return; /* the first match is all we need */                                                 \
                                                        }                                                                                                                                                \
                                                } else {                                                                                             \
                                                        new_find_extension("/", score, p->next_char, length + 1, spec + p->specificity, callerid, label, action);        \
                                                        if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {      \
-                                                       ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->exten :     \
+                                                       ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->name :      \
                                               "NULL");                                                                        \
                                                                return; /* the first match is all we need */                                                 \
                                                        }                                                                                                                                                \
@@ -1299,14 +1297,17 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
                                if (p->exten && *str2 != '/') {
                                        update_scoreboard(score, length + i, spec + (i * p->specificity), p->exten, '.', callerid, p->deleted, p);
                                        if (score->exten) {
-                                               ast_debug(4,"return because scoreboard has a match with '/'--- %s\n", score->exten->exten);
+                                               ast_debug(4, "return because scoreboard has a match with '/'--- %s\n",
+                                                       score->exten->name);
                                                return; /* the first match is all we need */
                                        }
                                }
                                if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
                                        new_find_extension("/", score, p->next_char, length + i, spec+(p->specificity*i), callerid, label, action);
                                        if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
-                                               ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set--- %s\n", score->exten ? score->exten->exten : "NULL");
+                                               ast_debug(4, "return because scoreboard has exact match OR "
+                                                       "CANMATCH/MATCHMORE & canmatch set--- %s\n",
+                                                       score->exten ? score->exten->name : "NULL");
                                                return; /* the first match is all we need */
                                        }
                                }
@@ -1321,14 +1322,17 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
                                if (p->exten && *str2 != '/') {
                                        update_scoreboard(score, length + 1, spec + (p->specificity * i), p->exten, '!', callerid, p->deleted, p);
                                        if (score->exten) {
-                                               ast_debug(4, "return because scoreboard has a '!' match--- %s\n", score->exten->exten);
+                                               ast_debug(4, "return because scoreboard has a '!' match--- %s\n",
+                                                       score->exten->name);
                                                return; /* the first match is all we need */
                                        }
                                }
                                if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
                                        new_find_extension("/", score, p->next_char, length + i, spec + (p->specificity * i), callerid, label, action);
                                        if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
-                                               ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n", score->exten ? score->exten->exten : "NULL");
+                                               ast_debug(4, "return because scoreboard has exact match OR "
+                                                       "CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n",
+                                                       score->exten ? score->exten->name : "NULL");
                                                return; /* the first match is all we need */
                                        }
                                }
@@ -1337,7 +1341,9 @@ static void new_find_extension(const char *str, struct scoreboard *score, struct
                                if (p->next_char && callerid && *callerid) {
                                        new_find_extension(callerid, score, p->next_char, length + 1, spec, callerid, label, action);
                                        if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
-                                               ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n", score->exten ? score->exten->exten : "NULL");
+                                               ast_debug(4, "return because scoreboard has exact match OR "
+                                                       "CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n",
+                                                       score->exten ? score->exten->name : "NULL");
                                                return; /* the first match is all we need */
                                        }
                                }
@@ -1690,7 +1696,7 @@ static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, str
                                }
                                if (m2->exten) {
                                        ast_log(LOG_WARNING, "Found duplicate exten. Had %s found %s\n",
-                                               m2->deleted ? "(deleted/invalid)" : m2->exten->exten, e1->exten);
+                                               m2->deleted ? "(deleted/invalid)" : m2->exten->name, e1->name);
                                }
                                m2->exten = e1;
                                m2->deleted = 0;
@@ -1716,7 +1722,7 @@ static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, str
                        if (!pat_node[idx_next].buf[0]) {
                                if (m2 && m2->exten) {
                                        ast_log(LOG_WARNING, "Found duplicate exten. Had %s found %s\n",
-                                               m2->deleted ? "(deleted/invalid)" : m2->exten->exten, e1->exten);
+                                               m2->deleted ? "(deleted/invalid)" : m2->exten->name, e1->name);
                                }
                                m1->deleted = 0;
                                m1->exten = e1;
@@ -2123,6 +2129,41 @@ static int ext_cmp(const char *left, const char *right)
        return ext_cmp_pattern(left + 1, right + 1);
 }
 
+static int ext_fluff_count(const char *exten)
+{
+       int fluff = 0;
+
+       if (*exten != '_') {
+               /* not a pattern, simple check. */
+               while (*exten) {
+                       if (*exten == '-') {
+                               fluff++;
+                       }
+                       exten++;
+               }
+
+               return fluff;
+       }
+
+       /* do pattern check */
+       while (*exten) {
+               if (*exten == '-') {
+                       fluff++;
+               } else if (*exten == '[') {
+                       /* skip set, dashes here matter. */
+                       exten = strchr(exten, ']');
+
+                       if (!exten) {
+                               /* we'll end up warning about this later, don't spam logs */
+                               return fluff;
+                       }
+               }
+               exten++;
+       }
+
+       return fluff;
+}
+
 int ast_extension_cmp(const char *a, const char *b)
 {
        int cmp;
@@ -2375,10 +2416,10 @@ struct fake_context /* this struct is purely for matching in the hashtab */
        struct ast_context *next;
        struct ast_includes includes;
        struct ast_ignorepats ignorepats;
+       struct ast_sws alts;
        const char *registrar;
        int refcount;
        int autohints;
-       AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
        ast_mutex_t macrolock;
        char name[256];
 };
@@ -2433,7 +2474,6 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
        int x, res;
        struct ast_context *tmp = NULL;
        struct ast_exten *e = NULL, *eroot = NULL;
-       struct ast_sw *sw = NULL;
        struct ast_exten pattern = {NULL, };
        struct scoreboard score = {0, };
        struct ast_str *tmpdata = NULL;
@@ -2657,23 +2697,28 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
        }
 
        /* Check alternative switches */
-       AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
-               struct ast_switch *asw = pbx_findswitch(sw->name);
+       for (idx = 0; idx < ast_context_switches_count(tmp); idx++) {
+               const struct ast_sw *sw = ast_context_switches_get(tmp, idx);
+               struct ast_switch *asw = pbx_findswitch(ast_get_switch_name(sw));
                ast_switch_f *aswf = NULL;
-               char *datap;
+               const char *datap;
 
                if (!asw) {
-                       ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+                       ast_log(LOG_WARNING, "No such switch '%s'\n", ast_get_switch_name(sw));
                        continue;
                }
 
                /* Substitute variables now */
-               if (sw->eval) {
+               if (ast_get_switch_eval(sw)) {
                        if (!(tmpdata = ast_str_thread_get(&switch_data, 512))) {
                                ast_log(LOG_WARNING, "Can't evaluate switch?!\n");
                                continue;
                        }
-                       pbx_substitute_variables_helper(chan, sw->data, ast_str_buffer(tmpdata), ast_str_size(tmpdata));
+                       pbx_substitute_variables_helper(chan, ast_get_switch_data(sw),
+                               ast_str_buffer(tmpdata), ast_str_size(tmpdata));
+                       datap = ast_str_buffer(tmpdata);
+               } else {
+                       datap = ast_get_switch_data(sw);
                }
 
                /* equivalent of extension_match_core() at the switch level */
@@ -2683,7 +2728,6 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
                        aswf = asw->matchmore;
                else /* action == E_MATCH */
                        aswf = asw->exists;
-               datap = sw->eval ? ast_str_buffer(tmpdata) : sw->data;
                if (!aswf)
                        res = 0;
                else {
@@ -3923,7 +3967,6 @@ static int ast_add_hint(struct ast_exten *e)
                        hint_new->last_presence_state = presence_state;
                        hint_new->last_presence_subtype = subtype;
                        hint_new->last_presence_message = message;
-                       message = subtype = NULL;
                }
        }
 
@@ -4257,8 +4300,10 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
        ast_channel_pbx(c)->rtimeoutms = 10000;
        ast_channel_pbx(c)->dtimeoutms = 5000;
 
+       ast_channel_lock(c);
        autoloopflag = ast_test_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP);       /* save value to restore at the end */
        ast_set_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP);
+       ast_channel_unlock(c);
 
        if (ast_strlen_zero(ast_channel_exten(c))) {
                /* If not successful fall back to 's' - but only if there is no given exten  */
@@ -4503,8 +4548,10 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
                ast_pbx_hangup_handler_run(c);
        }
 
+       ast_channel_lock(c);
        ast_set2_flag(ast_channel_flags(c), autoloopflag, AST_FLAG_IN_AUTOLOOP);
        ast_clear_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */
+       ast_channel_unlock(c);
        pbx_destroy(ast_channel_pbx(c));
        ast_channel_pbx_set(c, NULL);
 
@@ -4819,24 +4866,29 @@ int ast_context_remove_switch(const char *context, const char *sw, const char *d
  */
 int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
 {
-       struct ast_sw *i;
+       int idx;
        int ret = -1;
 
        ast_wrlock_context(con);
 
        /* walk switches */
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
-               if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
-                       (!registrar || !strcmp(i->registrar, registrar))) {
+       for (idx = 0; idx < ast_context_switches_count(con); idx++) {
+               struct ast_sw *i = AST_VECTOR_GET(&con->alts, idx);
+
+               if (!strcmp(ast_get_switch_name(i), sw) &&
+                       !strcmp(ast_get_switch_data(i), data) &&
+                       (!registrar || !strcmp(ast_get_switch_registrar(i), registrar))) {
+
                        /* found, remove from list */
                        ast_verb(3, "Removing switch '%s' from context '%s; registrar=%s'\n", sw, ast_get_context_name(con), registrar);
-                       AST_LIST_REMOVE_CURRENT(list);
-                       ast_free(i); /* free switch and return */
+                       AST_VECTOR_REMOVE_ORDERED(&con->alts, idx);
+
+                       /* free switch and return */
+                       sw_free(i);
                        ret = 0;
                        break;
                }
        }
-       AST_LIST_TRAVERSE_SAFE_END;
 
        ast_unlock_context(con);
 
@@ -4885,6 +4937,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
        struct ast_exten *peer;
        struct ast_exten ex, *exten2, *exten3;
        char dummy_name[1024];
+       char dummy_cid[1024];
        struct ast_exten *previous_peer = NULL;
        struct ast_exten *next_peer = NULL;
        int found = 0;
@@ -4900,9 +4953,14 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
 #endif
        /* find this particular extension */
        ex.exten = dummy_name;
+       ext_strncpy(dummy_name, extension, sizeof(dummy_name), 1);
        ex.matchcid = matchcallerid;
-       ex.cidmatch = callerid;
-       ast_copy_string(dummy_name, extension, sizeof(dummy_name));
+       if (callerid) {
+               ex.cidmatch = dummy_cid;
+               ext_strncpy(dummy_cid, callerid, sizeof(dummy_cid), 1);
+       } else {
+               ex.cidmatch = NULL;
+       }
        exten = ast_hashtab_lookup(con->root_table, &ex);
        if (exten) {
                if (priority == 0) {
@@ -4925,13 +4983,19 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
                        if (exten2) {
                                if (exten2->label) { /* if this exten has a label, remove that, too */
                                        exten3 = ast_hashtab_remove_this_object(exten->peer_label_table,exten2);
-                                       if (!exten3)
-                                               ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_table of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten);
+                                       if (!exten3) {
+                                               ast_log(LOG_ERROR, "Did not remove this priority label (%d/%s) "
+                                                       "from the peer_label_table of context %s, extension %s!\n",
+                                                       priority, exten2->label, con->name, exten2->name);
+                                       }
                                }
 
                                exten3 = ast_hashtab_remove_this_object(exten->peer_table, exten2);
-                               if (!exten3)
-                                       ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_table of context %s, extension %s!\n", priority, con->name, exten2->exten);
+                               if (!exten3) {
+                                       ast_log(LOG_ERROR, "Did not remove this priority (%d) from the "
+                                               "peer_table of context %s, extension %s!\n",
+                                               priority, con->name, exten2->name);
+                               }
                                if (exten2 == exten && exten2->peer) {
                                        exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
                                        ast_hashtab_insert_immediate(con->root_table, exten2->peer);
@@ -4940,8 +5004,11 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
                                        /* well, if the last priority of an exten is to be removed,
                                           then, the extension is removed, too! */
                                        exten3 = ast_hashtab_remove_this_object(con->root_table, exten);
-                                       if (!exten3)
-                                               ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_table (%s) (priority %d)\n", exten->exten, con->name, priority);
+                                       if (!exten3) {
+                                               ast_log(LOG_ERROR, "Did not remove this exten (%s) from the "
+                                                       "context root_table (%s) (priority %d)\n",
+                                                       exten->name, con->name, priority);
+                                       }
                                        if (con->pattern_tree) {
                                                struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
                                                if (x->exten) { /* this test for safety purposes */
@@ -4952,7 +5019,7 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
                                }
                        } else {
                                ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
-                                               priority, exten->exten, con->name);
+                                               priority, exten->name, con->name);
                        }
                }
        } else {
@@ -4969,10 +5036,12 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
 
        /* scan the extension list to find first matching extension-registrar */
        for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
-               if (!strcmp(exten->exten, extension) &&
-                       (!registrar || !strcmp(exten->registrar, registrar)) &&
-                       (!matchcallerid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(exten->cidmatch))))
+               if (!strcmp(exten->exten, ex.exten) &&
+                       (!matchcallerid ||
+                               (!ast_strlen_zero(ex.cidmatch) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, ex.cidmatch)) ||
+                               (ast_strlen_zero(ex.cidmatch) && ast_strlen_zero(exten->cidmatch)))) {
                        break;
+               }
        }
        if (!exten) {
                /* we can't find right extension */
@@ -4983,8 +5052,8 @@ int ast_context_remove_extension_callerid2(struct ast_context *con, const char *
 
        /* scan the priority list to remove extension with exten->priority == priority */
        for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next;
-                peer && !strcmp(peer->exten, extension) &&
-                       (!callerid || (!matchcallerid && !peer->matchcid) || (matchcallerid && peer->matchcid && !strcmp(peer->cidmatch, callerid))) ;
+                peer && !strcmp(peer->exten, ex.exten) &&
+                       (!callerid || (!matchcallerid && !peer->matchcid) || (matchcallerid && peer->matchcid && !strcmp(peer->cidmatch, ex.cidmatch))) ;
                        peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
 
                if ((priority == 0 || peer->priority == priority) &&
@@ -5375,6 +5444,21 @@ static void print_ext(struct ast_exten *e, char * buf, int buflen)
        }
 }
 
+/*! \brief Writes CLI output of a single extension for show dialplan */
+static void show_dialplan_helper_extension_output(int fd, char *buf1, char *buf2, struct ast_exten *exten)
+{
+       if (ast_get_extension_registrar_file(exten)) {
+               ast_cli(fd, "  %-17s %-45s [%s:%d]\n",
+                       buf1, buf2,
+                       ast_get_extension_registrar_file(exten),
+                       ast_get_extension_registrar_line(exten));
+               return;
+       }
+
+       ast_cli(fd, "  %-17s %-45s [%s]\n",
+               buf1, buf2, ast_get_extension_registrar(exten));
+}
+
 /* XXX not verified */
 static int show_dialplan_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, const struct ast_include *rinclude, int includecount, const char *includes[])
 {
@@ -5452,8 +5536,7 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
 
                        print_ext(e, buf2, sizeof(buf2));
 
-                       ast_cli(fd, "  %-17s %-45s [%s]\n", buf, buf2,
-                               ast_get_extension_registrar(e));
+                       show_dialplan_helper_extension_output(fd, buf, buf2, e);
 
                        dpc->total_exten++;
                        /* walk next extension peers */
@@ -5467,8 +5550,7 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
                                        buf[0] = '\0';
                                print_ext(p, buf2, sizeof(buf2));
 
-                               ast_cli(fd,"  %-17s %-45s [%s]\n", buf, buf2,
-                                       ast_get_extension_registrar(p));
+                               show_dialplan_helper_extension_output(fd, buf, buf2, p);
                        }
                }
 
@@ -5517,8 +5599,9 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
                        }
                }
                if (!rinclude) {
-                       struct ast_sw *sw = NULL;
-                       while ( (sw = ast_walk_context_switches(c, sw)) ) {
+                       for (idx = 0; idx < ast_context_switches_count(c); idx++) {
+                               const struct ast_sw *sw = ast_context_switches_get(c, idx);
+
                                snprintf(buf, sizeof(buf), "'%s/%s'",
                                        ast_get_switch_name(sw),
                                        ast_get_switch_data(sw));
@@ -5728,7 +5811,8 @@ static void manager_dpsendack(struct mansession *s, const struct message *m)
 static int manager_show_dialplan_helper(struct mansession *s, const struct message *m,
                                        const char *actionidtext, const char *context,
                                        const char *exten, struct dialplan_counters *dpc,
-                                       const struct ast_include *rinclude)
+                                       const struct ast_include *rinclude,
+                                       int includecount, const char *includes[])
 {
        struct ast_context *c;
        int res = 0, old_total_exten = dpc->total_exten;
@@ -5810,7 +5894,24 @@ static int manager_show_dialplan_helper(struct mansession *s, const struct messa
 
                        if (exten) {
                                /* Check all includes for the requested extension */
-                               manager_show_dialplan_helper(s, m, actionidtext, ast_get_include_name(i), exten, dpc, i);
+                               if (includecount >= AST_PBX_MAX_STACK) {
+                                       ast_log(LOG_WARNING, "Maximum include depth exceeded!\n");
+                               } else {
+                                       int dupe = 0;
+                                       int x;
+                                       for (x = 0; x < includecount; x++) {
+                                               if (!strcasecmp(includes[x], ast_get_include_name(i))) {
+                                                       dupe++;
+                                                       break;
+                                               }
+                                       }
+                                       if (!dupe) {
+                                               includes[includecount] = ast_get_include_name(i);
+                                               manager_show_dialplan_helper(s, m, actionidtext, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
+                                       } else {
+                                               ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
+                                       }
+                               }
                        } else {
                                if (!dpc->total_items++)
                                        manager_dpsendack(s, m);
@@ -5836,8 +5937,9 @@ static int manager_show_dialplan_helper(struct mansession *s, const struct messa
                        }
                }
                if (!rinclude) {
-                       struct ast_sw *sw = NULL;
-                       while ( (sw = ast_walk_context_switches(c, sw)) ) {
+                       for (idx = 0; idx < ast_context_switches_count(c); idx++) {
+                               const struct ast_sw *sw = ast_context_switches_get(c, idx);
+
                                if (!dpc->total_items++)
                                        manager_dpsendack(s, m);
                                astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
@@ -5865,6 +5967,7 @@ static int manager_show_dialplan(struct mansession *s, const struct message *m)
 {
        const char *exten, *context;
        const char *id = astman_get_header(m, "ActionID");
+       const char *incstack[AST_PBX_MAX_STACK];
        char idtext[256];
 
        /* Variables used for different counters */
@@ -5880,7 +5983,7 @@ static int manager_show_dialplan(struct mansession *s, const struct message *m)
        exten = astman_get_header(m, "Extension");
        context = astman_get_header(m, "Context");
 
-       manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL);
+       manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL, 0, incstack);
 
        if (!ast_strlen_zero(context) && !counters.context_existence) {
                char errorbuf[BUFSIZ];
@@ -6087,6 +6190,7 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
                tmp->registrar = ast_strdup(registrar);
                AST_VECTOR_INIT(&tmp->includes, 0);
                AST_VECTOR_INIT(&tmp->ignorepats, 0);
+               AST_VECTOR_INIT(&tmp->alts, 0);
                tmp->refcount = 1;
        } else {
                ast_log(LOG_ERROR, "Danger! We failed to allocate a context for %s!\n", name);
@@ -6138,7 +6242,6 @@ AST_LIST_HEAD_NOLOCK(store_hints, store_hint);
 static void context_merge_incls_swits_igps_other_registrars(struct ast_context *new, struct ast_context *old, const char *registrar)
 {
        int idx;
-       struct ast_sw *sw;
 
        ast_verb(3, "merging incls/swits/igpats from old(%s) to new(%s) context, registrar = %s\n", ast_get_context_name(old), ast_get_context_name(new), registrar);
        /* copy in the includes, switches, and ignorepats */
@@ -6153,9 +6256,12 @@ static void context_merge_incls_swits_igps_other_registrars(struct ast_context *
        }
 
        /* walk through switches */
-       for (sw = NULL; (sw = ast_walk_context_switches(old, sw)) ; ) {
-               if (strcmp(ast_get_switch_registrar(sw), registrar) == 0)
+       for (idx = 0; idx < ast_context_switches_count(old); idx++) {
+               const struct ast_sw *sw = ast_context_switches_get(old, idx);
+
+               if (!strcmp(ast_get_switch_registrar(sw), registrar)) {
                        continue; /* not mine */
+               }
                ast_context_add_switch2(new, ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_eval(sw), ast_get_switch_registrar(sw));
        }
 
@@ -6268,11 +6374,12 @@ static void context_merge(struct ast_context **extcontexts, struct ast_hashtab *
 
                                dupdstr = ast_strdup(prio_item->data);
 
-                               res1 = ast_add_extension2(new, 0, prio_item->exten, prio_item->priority, prio_item->label,
-                                                                                 prio_item->matchcid ? prio_item->cidmatch : NULL, prio_item->app, dupdstr, ast_free_ptr, prio_item->registrar);
+                               res1 = ast_add_extension2(new, 0, prio_item->name, prio_item->priority, prio_item->label,
+                                                                                 prio_item->matchcid ? prio_item->cidmatch : NULL, prio_item->app, dupdstr, ast_free_ptr, prio_item->registrar,
+                                                                                 prio_item->registrar_file, prio_item->registrar_line);
                                if (!res1 && new_exten_item && new_prio_item){
                                        ast_verb(3,"Dropping old dialplan item %s/%s/%d [%s(%s)] (registrar=%s) due to conflict with new dialplan\n",
-                                                       context->name, prio_item->exten, prio_item->priority, prio_item->app, (char*)prio_item->data, prio_item->registrar);
+                                                       context->name, prio_item->name, prio_item->priority, prio_item->app, (char*)prio_item->data, prio_item->registrar);
                                } else {
                                        /* we do NOT pass the priority data from the old to the new -- we pass a copy of it, so no changes to the current dialplan take place,
                                         and no double frees take place, either! */
@@ -6585,7 +6692,11 @@ int ast_context_add_include2(struct ast_context *con, const char *value,
        }
 
        /* ... include new context into context list, unlock, return */
-       AST_VECTOR_APPEND(&con->includes, new_include);
+       if (AST_VECTOR_APPEND(&con->includes, new_include)) {
+               include_free(new_include);
+               ast_unlock_context(con);
+               return -1;
+       }
        ast_verb(3, "Including context '%s' in context '%s'\n",
                ast_get_include_name(new_include), ast_get_context_name(con));
 
@@ -6622,43 +6733,24 @@ int ast_context_add_switch(const char *context, const char *sw, const char *data
 int ast_context_add_switch2(struct ast_context *con, const char *value,
        const char *data, int eval, const char *registrar)
 {
+       int idx;
        struct ast_sw *new_sw;
-       struct ast_sw *i;
-       int length;
-       char *p;
-
-       length = sizeof(struct ast_sw);
-       length += strlen(value) + 1;
-       if (data)
-               length += strlen(data);
-       length++;
 
        /* allocate new sw structure ... */
-       if (!(new_sw = ast_calloc(1, length)))
+       if (!(new_sw = sw_alloc(value, data, eval, registrar))) {
                return -1;
-       /* ... fill in this structure ... */
-       p = new_sw->stuff;
-       new_sw->name = p;
-       strcpy(new_sw->name, value);
-       p += strlen(value) + 1;
-       new_sw->data = p;
-       if (data) {
-               strcpy(new_sw->data, data);
-               p += strlen(data) + 1;
-       } else {
-               strcpy(new_sw->data, "");
-               p++;
        }
-       new_sw->eval      = eval;
-       new_sw->registrar = registrar;
 
        /* ... try to lock this context ... */
        ast_wrlock_context(con);
 
        /* ... go to last sw and check if context is already swd too... */
-       AST_LIST_TRAVERSE(&con->alts, i, list) {
-               if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
-                       ast_free(new_sw);
+       for (idx = 0; idx < ast_context_switches_count(con); idx++) {
+               const struct ast_sw *i = ast_context_switches_get(con, idx);
+
+               if (!strcasecmp(ast_get_switch_name(i), ast_get_switch_name(new_sw)) &&
+                       !strcasecmp(ast_get_switch_data(i), ast_get_switch_data(new_sw))) {
+                       sw_free(new_sw);
                        ast_unlock_context(con);
                        errno = EEXIST;
                        return -1;
@@ -6666,9 +6758,14 @@ int ast_context_add_switch2(struct ast_context *con, const char *value,
        }
 
        /* ... sw new context into context list, unlock, return */
-       AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
+       if (AST_VECTOR_APPEND(&con->alts, new_sw)) {
+               sw_free(new_sw);
+               ast_unlock_context(con);
+               return -1;
+       }
 
-       ast_verb(3, "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+       ast_verb(3, "Including switch '%s/%s' in context '%s'\n",
+               ast_get_switch_name(new_sw), ast_get_switch_data(new_sw), ast_get_context_name(con));
 
        ast_unlock_context(con);
 
@@ -6753,7 +6850,11 @@ int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const
                        return -1;
                }
        }
-       AST_VECTOR_APPEND(&con->ignorepats, ignorepat);
+       if (AST_VECTOR_APPEND(&con->ignorepats, ignorepat)) {
+               ignorepat_free(ignorepat);
+               ast_unlock_context(con);
+               return -1;
+       }
        ast_unlock_context(con);
 
        return 0;
@@ -6798,7 +6899,7 @@ static int ast_add_extension_nolock(const char *context, int replace, const char
        c = find_context(context);
        if (c) {
                ret = ast_add_extension2_lockopt(c, replace, extension, priority, label, callerid,
-                       application, data, datad, registrar, 1);
+                       application, data, datad, registrar, NULL, 0, 1);
        }
 
        return ret;
@@ -6818,7 +6919,7 @@ int ast_add_extension(const char *context, int replace, const char *extension,
        c = find_context_locked(context);
        if (c) {
                ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
-                       application, data, datad, registrar);
+                       application, data, datad, registrar, NULL, 0);
                ast_unlock_contexts();
        }
 
@@ -6895,29 +6996,51 @@ int ast_async_goto_by_name(const char *channame, const char *context, const char
        return res;
 }
 
-/*! \brief copy a string skipping whitespace */
-static int ext_strncpy(char *dst, const char *src, int len)
+/*!
+ * \internal
+ * \brief Copy a string skipping whitespace and optionally dashes.
+ *
+ * \param dst Destination buffer to copy src string.
+ * \param src Null terminated string to copy.
+ * \param dst_size Number of bytes in the dst buffer.
+ * \param nofluf Nonzero if '-' chars are not copied.
+ *
+ * \return Number of bytes written to dst including null terminator.
+ */
+static unsigned int ext_strncpy(char *dst, const char *src, size_t dst_size, int nofluff)
 {
-       int count = 0;
-       int insquares = 0;
+       unsigned int count;
+       unsigned int insquares;
+       unsigned int is_pattern;
 
-       while (*src && (count < len - 1)) {
+       if (!dst_size--) {
+               /* There really is no dst buffer */
+               return 0;
+       }
+
+       count = 0;
+       insquares = 0;
+       is_pattern = *src == '_';
+       while (*src && count < dst_size) {
                if (*src == '[') {
-                       insquares = 1;
+                       if (is_pattern) {
+                               insquares = 1;
+                       }
                } else if (*src == ']') {
                        insquares = 0;
                } else if (*src == ' ' && !insquares) {
-                       src++;
+                       ++src;
+                       continue;
+               } else if (*src == '-' && !insquares && nofluff) {
+                       ++src;
                        continue;
                }
-               *dst = *src;
-               dst++;
-               src++;
-               count++;
+               *dst++ = *src++;
+               ++count;
        }
        *dst = '\0';
 
-       return count;
+       return count + 1;
 }
 
 /*!
@@ -6934,14 +7057,14 @@ static int add_priority(struct ast_context *con, struct ast_exten *tmp,
 
        for (ep = NULL; e ; ep = e, e = e->peer) {
                if (e->label && tmp->label && e->priority != tmp->priority && !strcmp(e->label, tmp->label)) {
-                       if (strcmp(e->exten, tmp->exten)) {
+                       if (strcmp(e->name, tmp->name)) {
                                ast_log(LOG_WARNING,
                                        "Extension '%s' priority %d in '%s', label '%s' already in use at aliased extension '%s' priority %d\n",
-                                       tmp->exten, tmp->priority, con->name, tmp->label, e->exten, e->priority);
+                                       tmp->name, tmp->priority, con->name, tmp->label, e->name, e->priority);
                        } else {
                                ast_log(LOG_WARNING,
                                        "Extension '%s' priority %d in '%s', label '%s' already in use at priority %d\n",
-                                       tmp->exten, tmp->priority, con->name, tmp->label, e->priority);
+                                       tmp->name, tmp->priority, con->name, tmp->label, e->priority);
                        }
                        repeated_label = 1;
                }
@@ -6967,14 +7090,14 @@ static int add_priority(struct ast_context *con, struct ast_exten *tmp,
                /* Can't have something exactly the same.  Is this a
                   replacement?  If so, replace, otherwise, bonk. */
                if (!replace) {
-                       if (strcmp(e->exten, tmp->exten)) {
+                       if (strcmp(e->name, tmp->name)) {
                                ast_log(LOG_WARNING,
                                        "Unable to register extension '%s' priority %d in '%s', already in use by aliased extension '%s'\n",
-                                       tmp->exten, tmp->priority, con->name, e->exten);
+                                       tmp->name, tmp->priority, con->name, e->name);
                        } else {
                                ast_log(LOG_WARNING,
                                        "Unable to register extension '%s' priority %d in '%s', already in use\n",
-                                       tmp->exten, tmp->priority, con->name);
+                                       tmp->name, tmp->priority, con->name);
                        }
 
                        return -1;
@@ -7118,19 +7241,19 @@ static int add_priority(struct ast_context *con, struct ast_exten *tmp,
 int ast_add_extension2(struct ast_context *con,
        int replace, const char *extension, int priority, const char *label, const char *callerid,
        const char *application, void *data, void (*datad)(void *),
-       const char *registrar)
+       const char *registrar, const char *registrar_file, int registrar_line)
 {
        return ast_add_extension2_lockopt(con, replace, extension, priority, label, callerid,
-               application, data, datad, registrar, 1);
+               application, data, datad, registrar, registrar_file, registrar_line, 1);
 }
 
 int ast_add_extension2_nolock(struct ast_context *con,
        int replace, const char *extension, int priority, const char *label, const char *callerid,
        const char *application, void *data, void (*datad)(void *),
-       const char *registrar)
+       const char *registrar, const char *registrar_file, int registrar_line)
 {
        return ast_add_extension2_lockopt(con, replace, extension, priority, label, callerid,
-               application, data, datad, registrar, 0);
+               application, data, datad, registrar, registrar_file, registrar_line, 0);
 }
 
 
@@ -7144,7 +7267,7 @@ int ast_add_extension2_nolock(struct ast_context *con,
 static int ast_add_extension2_lockopt(struct ast_context *con,
        int replace, const char *extension, int priority, const char *label, const char *callerid,
        const char *application, void *data, void (*datad)(void *),
-       const char *registrar, int lock_context)
+       const char *registrar, const char *registrar_file, int registrar_line, int lock_context)
 {
        /*
         * Sort extensions (or patterns) according to the rules indicated above.
@@ -7159,6 +7282,8 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
        char expand_buf[VAR_BUF_SIZE];
        struct ast_exten dummy_exten = {0};
        char dummy_name[1024];
+       int exten_fluff;
+       int callerid_fluff;
 
        if (ast_strlen_zero(extension)) {
                ast_log(LOG_ERROR,"You have to be kidding-- add exten '' to context %s? Figure out a name and call me back. Action ignored.\n",
@@ -7168,28 +7293,54 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
 
        /* If we are adding a hint evalulate in variables and global variables */
        if (priority == PRIORITY_HINT && strstr(application, "${") && extension[0] != '_') {
+               int inhibited;
                struct ast_channel *c = ast_dummy_channel_alloc();
 
                if (c) {
                        ast_channel_exten_set(c, extension);
                        ast_channel_context_set(c, con->name);
                }
+
+               /*
+                * We can allow dangerous functions when adding a hint since
+                * altering dialplan is itself a privileged activity.  Otherwise,
+                * we could never execute dangerous functions.
+                */
+               inhibited = ast_thread_inhibit_escalations_swap(0);
                pbx_substitute_variables_helper(c, application, expand_buf, sizeof(expand_buf));
+               if (0 < inhibited) {
+                       ast_thread_inhibit_escalations();
+               }
+
                application = expand_buf;
                if (c) {
                        ast_channel_unref(c);
                }
        }
 
+       exten_fluff = ext_fluff_count(extension);
+       callerid_fluff = callerid ? ext_fluff_count(callerid) : 0;
+
        length = sizeof(struct ast_exten);
        length += strlen(extension) + 1;
+       if (exten_fluff) {
+               length += strlen(extension) + 1 - exten_fluff;
+       }
        length += strlen(application) + 1;
-       if (label)
+       if (label) {
                length += strlen(label) + 1;
-       if (callerid)
+       }
+       if (callerid) {
                length += strlen(callerid) + 1;
-       else
+               if (callerid_fluff) {
+                       length += strlen(callerid) + 1 - callerid_fluff;
+               }
+       } else {
                length ++;      /* just the '\0' */
+       }
+       if (registrar_file) {
+               length += strlen(registrar_file) + 1;
+       }
 
        /* Be optimistic:  Build the extension structure first */
        if (!(tmp = ast_calloc(1, length)))
@@ -7205,25 +7356,46 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
                strcpy(p, label);
                p += strlen(label) + 1;
        }
-       tmp->exten = p;
-       p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
+       tmp->name = p;
+       p += ext_strncpy(p, extension, strlen(extension) + 1, 0);
+       if (exten_fluff) {
+               tmp->exten = p;
+               p += ext_strncpy(p, extension, strlen(extension) + 1 - exten_fluff, 1);
+       } else {
+               /* no fluff, we don't need a copy. */
+               tmp->exten = tmp->name;
+       }
        tmp->priority = priority;
-       tmp->cidmatch = p;      /* but use p for assignments below */
+       tmp->cidmatch_display = tmp->cidmatch = p;      /* but use p for assignments below */
 
        /* Blank callerid and NULL callerid are two SEPARATE things.  Do NOT confuse the two!!! */
        if (callerid) {
-               p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
+               p += ext_strncpy(p, callerid, strlen(callerid) + 1, 0);
+               if (callerid_fluff) {
+                       tmp->cidmatch = p;
+                       p += ext_strncpy(p, callerid, strlen(callerid) + 1 - callerid_fluff, 1);
+               }
                tmp->matchcid = AST_EXT_MATCHCID_ON;
        } else {
                *p++ = '\0';
                tmp->matchcid = AST_EXT_MATCHCID_OFF;
        }
+
+       if (registrar_file) {
+               tmp->registrar_file = p;
+               strcpy(p, registrar_file);
+               p += strlen(registrar_file) + 1;
+       } else {
+               tmp->registrar_file = NULL;
+       }
+
        tmp->app = p;
        strcpy(p, application);
        tmp->parent = con;
        tmp->data = data;
        tmp->datad = datad;
        tmp->registrar = registrar;
+       tmp->registrar_line = registrar_line;
 
        if (lock_context) {
                ast_wrlock_context(con);
@@ -7231,7 +7403,7 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
 
        if (con->pattern_tree) { /* usually, on initial load, the pattern_tree isn't formed until the first find_exten; so if we are adding
                                                                an extension, and the trie exists, then we need to incrementally add this pattern to it. */
-               ast_copy_string(dummy_name, extension, sizeof(dummy_name));
+               ext_strncpy(dummy_name, tmp->exten, sizeof(dummy_name), 1);
                dummy_exten.exten = dummy_name;
                dummy_exten.matchcid = AST_EXT_MATCHCID_OFF;
                dummy_exten.cidmatch = 0;
@@ -7293,52 +7465,38 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
                 * so insert in the main list right before 'e' (if any)
                 */
                tmp->next = e;
-               if (el) {  /* there is another exten already in this context */
-                       el->next = tmp;
-                       tmp->peer_table = ast_hashtab_create(13,
-                                                       hashtab_compare_exten_numbers,
+               tmp->peer_table = ast_hashtab_create(13,
+                                               hashtab_compare_exten_numbers,
+                                               ast_hashtab_resize_java,
+                                               ast_hashtab_newsize_java,
+                                               hashtab_hash_priority,
+                                               0);
+               tmp->peer_label_table = ast_hashtab_create(7,
+                                                       hashtab_compare_exten_labels,
                                                        ast_hashtab_resize_java,
                                                        ast_hashtab_newsize_java,
-                                                       hashtab_hash_priority,
+                                                       hashtab_hash_labels,
                                                        0);
-                       tmp->peer_label_table = ast_hashtab_create(7,
-                                                               hashtab_compare_exten_labels,
-                                                               ast_hashtab_resize_java,
-                                                               ast_hashtab_newsize_java,
-                                                               hashtab_hash_labels,
-                                                               0);
-                       if (label) {
-                               ast_hashtab_insert_safe(tmp->peer_label_table, tmp);
-                       }
-                       ast_hashtab_insert_safe(tmp->peer_table, tmp);
+
+               if (el) {  /* there is another exten already in this context */
+                       el->next = tmp;
                } else {  /* this is the first exten in this context */
-                       if (!con->root_table)
+                       if (!con->root_table) {
                                con->root_table = ast_hashtab_create(27,
                                                                                                        hashtab_compare_extens,
                                                                                                        ast_hashtab_resize_java,
                                                                                                        ast_hashtab_newsize_java,
                                                                                                        hashtab_hash_extens,
                                                                                                        0);
-                       con->root = tmp;
-                       con->root->peer_table = ast_hashtab_create(13,
-                                                               hashtab_compare_exten_numbers,
-                                                               ast_hashtab_resize_java,
-                                                               ast_hashtab_newsize_java,
-                                                               hashtab_hash_priority,
-                                                               0);
-                       con->root->peer_label_table = ast_hashtab_create(7,
-                                                                       hashtab_compare_exten_labels,
-                                                                       ast_hashtab_resize_java,
-                                                                       ast_hashtab_newsize_java,
-                                                                       hashtab_hash_labels,
-                                                                       0);
-                       if (label) {
-                               ast_hashtab_insert_safe(con->root->peer_label_table, tmp);
                        }
-                       ast_hashtab_insert_safe(con->root->peer_table, tmp);
-
+                       con->root = tmp;
                }
+               if (label) {
+                       ast_hashtab_insert_safe(tmp->peer_label_table, tmp);
+               }
+               ast_hashtab_insert_safe(tmp->peer_table, tmp);
                ast_hashtab_insert_safe(con->root_table, tmp);
+
                if (lock_context) {
                        ast_unlock_context(con);
                }
@@ -7349,19 +7507,19 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
        if (option_debug) {
                if (tmp->matchcid == AST_EXT_MATCHCID_ON) {
                        ast_debug(1, "Added extension '%s' priority %d (CID match '%s') to %s (%p)\n",
-                                         tmp->exten, tmp->priority, tmp->cidmatch, con->name, con);
+                                         tmp->name, tmp->priority, tmp->cidmatch_display, con->name, con);
                } else {
                        ast_debug(1, "Added extension '%s' priority %d to %s (%p)\n",
-                                         tmp->exten, tmp->priority, con->name, con);
+                                         tmp->name, tmp->priority, con->name, con);
                }
        }
 
        if (tmp->matchcid == AST_EXT_MATCHCID_ON) {
                ast_verb(3, "Added extension '%s' priority %d (CID match '%s') to %s\n",
-                                tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+                                tmp->name, tmp->priority, tmp->cidmatch_display, con->name);
        } else {
                ast_verb(3, "Added extension '%s' priority %d to %s\n",
-                                tmp->exten, tmp->priority, con->name);
+                                tmp->name, tmp->priority, con->name);
        }
 
        return 0;
@@ -7387,8 +7545,8 @@ struct pbx_outgoing {
        int dial_res;
        /*! \brief Set when dialing is completed */
        unsigned int dialed:1;
-       /*! \brief Set when execution is completed */
-       unsigned int executed:1;
+       /*! \brief Set if we've spawned a thread to do our work */
+       unsigned int in_separate_thread:1;
 };
 
 /*! \brief Destructor for outgoing structure */
@@ -7411,13 +7569,19 @@ static void *pbx_outgoing_exec(void *data)
        RAII_VAR(struct pbx_outgoing *, outgoing, data, ao2_cleanup);
        enum ast_dial_result res;
 
-       /* Notify anyone interested that dialing is complete */
        res = ast_dial_run(outgoing->dial, NULL, 0);
-       ao2_lock(outgoing);
-       outgoing->dial_res = res;
-       outgoing->dialed = 1;
-       ast_cond_signal(&outgoing->cond);
-       ao2_unlock(outgoing);
+
+       if (outgoing->in_separate_thread) {
+               /* Notify anyone interested that dialing is complete */
+               ao2_lock(outgoing);
+               outgoing->dial_res = res;
+               outgoing->dialed = 1;
+               ast_cond_signal(&outgoing->cond);
+               ao2_unlock(outgoing);
+       } else {
+               /* We still need the dial result, but we don't need to lock */
+               outgoing->dial_res = res;
+       }
 
        /* If the outgoing leg was not answered we can immediately return and go no further */
        if (res != AST_DIAL_RESULT_ANSWERED) {
@@ -7457,12 +7621,6 @@ static void *pbx_outgoing_exec(void *data)
                }
        }
 
-       /* Notify anyone else again that may be interested that execution is complete */
-       ao2_lock(outgoing);
-       outgoing->executed = 1;
-       ast_cond_signal(&outgoing->cond);
-       ao2_unlock(outgoing);
-
        return NULL;
 }
 
@@ -7537,11 +7695,13 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap,
        const char *app, const char *appdata, int *reason, int synchronous,
        const char *cid_num, const char *cid_name, struct ast_variable *vars,
        const char *account, struct ast_channel **locked_channel, int early_media,
-       const struct ast_assigned_ids *assignedids)
+       const struct ast_assigned_ids *assignedids, const char *predial_callee)
 {
        RAII_VAR(struct pbx_outgoing *, outgoing, NULL, ao2_cleanup);
        struct ast_channel *dialed;
        pthread_t thread;
+       char tmp_cid_name[128];
+       char tmp_cid_num[128];
 
        outgoing = ao2_alloc(sizeof(*outgoing), pbx_outgoing_destroy);
        if (!outgoing) {
@@ -7568,6 +7728,11 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap,
 
        ast_dial_set_global_timeout(outgoing->dial, timeout);
 
+       if (!ast_strlen_zero(predial_callee)) {
+               /* note casting to void * here to suppress compiler warning message (passing const to non-const function) */
+               ast_dial_option_global_enable(outgoing->dial, AST_DIAL_OPTION_PREDIAL, (void *)predial_callee);
+       }
+
        if (ast_dial_prerun(outgoing->dial, NULL, cap)) {
                if (synchronous && reason) {
                        *reason = pbx_dial_reason(AST_DIAL_RESULT_FAILED,
@@ -7592,6 +7757,25 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap,
                ast_channel_stage_snapshot_done(dialed);
        }
        ast_set_flag(ast_channel_flags(dialed), AST_FLAG_ORIGINATED);
+
+       if (!ast_strlen_zero(predial_callee)) {
+               char *tmp = NULL;
+               /*
+                * The predial sub routine may have set callerid so set this into the new channel
+                * Note... cid_num and cid_name parameters to this function will always be NULL if
+                * predial_callee is non-NULL so we are not overwriting anything here.
+                */
+               tmp = S_COR(ast_channel_caller(dialed)->id.number.valid, ast_channel_caller(dialed)->id.number.str, NULL);
+               if (tmp) {
+                       ast_copy_string(tmp_cid_num, tmp, sizeof(tmp_cid_num));
+                       cid_num = tmp_cid_num;
+               }
+               tmp = S_COR(ast_channel_caller(dialed)->id.name.valid, ast_channel_caller(dialed)->id.name.str, NULL);
+               if (tmp) {
+                       ast_copy_string(tmp_cid_name, tmp, sizeof(tmp_cid_name));
+                       cid_name = tmp_cid_name;
+               }
+       }
        ast_channel_unlock(dialed);
 
        if (!ast_strlen_zero(cid_num) || !ast_strlen_zero(cid_name)) {
@@ -7642,34 +7826,42 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap,
                }
        }
 
+       /* This extra reference is dereferenced by pbx_outgoing_exec */
        ao2_ref(outgoing, +1);
-       if (ast_pthread_create_detached(&thread, NULL, pbx_outgoing_exec, outgoing)) {
-               ast_log(LOG_WARNING, "Unable to spawn dialing thread for '%s/%s'\n", type, addr);
-               ao2_ref(outgoing, -1);
-               if (locked_channel) {
-                       if (!synchronous) {
-                               ast_channel_unlock(dialed);
+
+       if (synchronous == AST_OUTGOING_WAIT_COMPLETE) {
+               /*
+                * Because we are waiting until this is complete anyway, there is no
+                * sense in creating another thread that we will just need to wait
+                * for, so instead we commandeer the current thread.
+                */
+               pbx_outgoing_exec(outgoing);
+       } else {
+               outgoing->in_separate_thread = 1;
+
+               if (ast_pthread_create_detached(&thread, NULL, pbx_outgoing_exec, outgoing)) {
+                       ast_log(LOG_WARNING, "Unable to spawn dialing thread for '%s/%s'\n", type, addr);
+                       ao2_ref(outgoing, -1);
+                       if (locked_channel) {
+                               if (!synchronous) {
+                                       ast_channel_unlock(dialed);
+                               }
+                               ast_channel_unref(dialed);
                        }
-                       ast_channel_unref(dialed);
+                       return -1;
                }
-               return -1;
-       }
 
-       if (synchronous) {
-               ao2_lock(outgoing);
-               /* Wait for dialing to complete */
-               while (!outgoing->dialed) {
-                       ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing));
-               }
-               if (1 < synchronous
-                       && outgoing->dial_res == AST_DIAL_RESULT_ANSWERED) {
-                       /* Wait for execution to complete */
-                       while (!outgoing->executed) {
+               if (synchronous) {
+                       ao2_lock(outgoing);
+                       /* Wait for dialing to complete */
+                       while (!outgoing->dialed) {
                                ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing));
                        }
+                       ao2_unlock(outgoing);
                }
-               ao2_unlock(outgoing);
+       }
 
+       if (synchronous) {
                /* Determine the outcome of the dialing attempt up to it being answered. */
                if (reason) {
                        *reason = pbx_dial_reason(outgoing->dial_res,
@@ -7700,6 +7892,16 @@ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const c
        const char *account, struct ast_channel **locked_channel, int early_media,
        const struct ast_assigned_ids *assignedids)
 {
+       return ast_pbx_outgoing_exten_predial(type, cap, addr, timeout, context, exten, priority, reason,
+               synchronous, cid_num, cid_name, vars, account, locked_channel, early_media, assignedids, NULL);
+}
+
+int ast_pbx_outgoing_exten_predial(const char *type, struct ast_format_cap *cap, const char *addr,
+       int timeout, const char *context, const char *exten, int priority, int *reason,
+       int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars,
+       const char *account, struct ast_channel **locked_channel, int early_media,
+       const struct ast_assigned_ids *assignedids, const char *predial_callee)
+{
        int res;
        int my_reason;
 
@@ -7713,10 +7915,10 @@ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const c
 
        res = pbx_outgoing_attempt(type, cap, addr, timeout, context, exten, priority,
                NULL, NULL, reason, synchronous, cid_num, cid_name, vars, account, locked_channel,
-               early_media, assignedids);
+               early_media, assignedids, predial_callee);
 
        if (res < 0 /* Call failed to get connected for some reason. */
-               && 1 < synchronous
+               && 0 < synchronous
                && ast_exists_extension(NULL, context, "failed", 1, NULL)) {
                struct ast_channel *failed;
 
@@ -7754,6 +7956,16 @@ int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const cha
        const char *account, struct ast_channel **locked_channel,
        const struct ast_assigned_ids *assignedids)
 {
+       return ast_pbx_outgoing_app_predial(type, cap, addr, timeout, app, appdata, reason, synchronous,
+               cid_num, cid_name, vars, account, locked_channel, assignedids, NULL);
+}
+
+int ast_pbx_outgoing_app_predial(const char *type, struct ast_format_cap *cap, const char *addr,
+       int timeout, const char *app, const char *appdata, int *reason, int synchronous,
+       const char *cid_num, const char *cid_name, struct ast_variable *vars,
+       const char *account, struct ast_channel **locked_channel,
+       const struct ast_assigned_ids *assignedids, const char *predial_callee)
+{
        if (reason) {
                *reason = 0;
        }
@@ -7766,7 +7978,7 @@ int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const cha
 
        return pbx_outgoing_attempt(type, cap, addr, timeout, NULL, NULL, 0, app, appdata,
                reason, synchronous, cid_num, cid_name, vars, account, locked_channel, 0,
-               assignedids);
+               assignedids, predial_callee);
 }
 
 /* this is the guts of destroying a context --
@@ -7775,7 +7987,6 @@ int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const cha
 
 static void __ast_internal_context_destroy( struct ast_context *con)
 {
-       struct ast_sw *sw;
        struct ast_exten *e, *el, *en;
        struct ast_context *tmp = con;
 
@@ -7787,6 +7998,10 @@ static void __ast_internal_context_destroy( struct ast_context *con)
        AST_VECTOR_CALLBACK_VOID(&tmp->ignorepats, ignorepat_free);
        AST_VECTOR_FREE(&tmp->ignorepats);
 
+       /* Free switches */
+       AST_VECTOR_CALLBACK_VOID(&tmp->alts, sw_free);
+       AST_VECTOR_FREE(&tmp->alts);
+
        if (tmp->registrar)
                ast_free(tmp->registrar);
 
@@ -7798,8 +8013,6 @@ static void __ast_internal_context_destroy( struct ast_context *con)
        if (tmp->pattern_tree)
                destroy_pattern_tree(tmp->pattern_tree);
 
-       while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
-               ast_free(sw);
        for (e = tmp->root; e;) {
                for (en = e->peer; en;) {
                        el = en;
@@ -7846,7 +8059,6 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
                        /* then search thru and remove any extens that match registrar. */
                        struct ast_hashtab_iter *exten_iter;
                        struct ast_hashtab_iter *prio_iter;
-                       struct ast_sw *sw = NULL;
                        int idx;
 
                        /* remove any ignorepats whose registrar matches */
@@ -7868,13 +8080,14 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
                                }
                        }
                        /* remove any switches whose registrar matches */
-                       AST_LIST_TRAVERSE_SAFE_BEGIN(&tmp->alts, sw, list) {
-                               if (strcmp(sw->registrar,registrar) == 0) {
-                                       AST_LIST_REMOVE_CURRENT(list);
-                                       ast_free(sw);
+                       for (idx = ast_context_switches_count(tmp) - 1; idx >= 0; idx--) {
+                               struct ast_sw *sw = AST_VECTOR_GET(&tmp->alts, idx);
+
+                               if (!strcmp(ast_get_switch_registrar(sw), registrar)) {
+                                       AST_VECTOR_REMOVE_ORDERED(&tmp->alts, idx);
+                                       sw_free(sw);
                                }
                        }
-                       AST_LIST_TRAVERSE_SAFE_END;
 
                        if (tmp->root_table) { /* it is entirely possible that the context is EMPTY */
                                exten_iter = ast_hashtab_start_traversal(tmp->root_table);
@@ -7898,7 +8111,7 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
                                                        continue;
                                                }
                                                ast_verb(3, "Remove %s/%s/%d, registrar=%s; con=%s(%p); con->root=%p\n",
-                                                                tmp->name, prio_item->exten, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL);
+                                                                tmp->name, prio_item->name, prio_item->priority, registrar, con? con->name : "<nil>", con, con? con->root_table: NULL);
                                                ast_copy_string(extension, prio_item->exten, sizeof(extension));
                                                if (prio_item->cidmatch) {
                                                        ast_copy_string(cidmatch, prio_item->cidmatch, sizeof(cidmatch));
@@ -7925,7 +8138,7 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
                        /* delete the context if it's registrar matches, is empty, has refcount of 1, */
                        /* it's not empty, if it has includes, ignorepats, or switches that are registered from
                           another registrar. It's not empty if there are any extensions */
-                       if (strcmp(tmp->registrar, registrar) == 0 && tmp->refcount < 2 && !tmp->root && !ast_context_ignorepats_count(tmp) && !ast_context_includes_count(tmp) && AST_LIST_EMPTY(&tmp->alts)) {
+                       if (strcmp(tmp->registrar, registrar) == 0 && tmp->refcount < 2 && !tmp->root && !ast_context_ignorepats_count(tmp) && !ast_context_includes_count(tmp) && !ast_context_switches_count(tmp)) {
                                ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar);
                                ast_hashtab_remove_this_object(contexttab, tmp);
 
@@ -8105,56 +8318,6 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
        ast_free(hint_app);
 }
 
-/*!
- * \internal
- * \brief Implements the hints data provider.
- */
-static int hints_data_provider_get(const struct ast_data_search *search,
-       struct ast_data *data_root)
-{
-       struct ast_data *data_hint;
-       struct ast_hint *hint;
-       int watchers;
-       struct ao2_iterator i;
-
-       if (ao2_container_count(hints) == 0) {
-               return 0;
-       }
-
-       i = ao2_iterator_init(hints, 0);
-       for (; (hint = ao2_iterator_next(&i)); ao2_ref(hint, -1)) {
-               watchers = ao2_container_count(hint->callbacks);
-               data_hint = ast_data_add_node(data_root, "hint");
-               if (!data_hint) {
-                       continue;
-               }
-               ast_data_add_str(data_hint, "extension", ast_get_extension_name(hint->exten));
-               ast_data_add_str(data_hint, "context", ast_get_context_name(ast_get_extension_context(hint->exten)));
-               ast_data_add_str(data_hint, "application", ast_get_extension_app(hint->exten));
-               ast_data_add_str(data_hint, "state", ast_extension_state2str(hint->laststate));
-               ast_data_add_str(data_hint, "presence_state", ast_presence_state2str(hint->last_presence_state));
-               ast_data_add_str(data_hint, "presence_subtype", S_OR(hint->last_presence_subtype, ""));
-               ast_data_add_str(data_hint, "presence_subtype", S_OR(hint->last_presence_message, ""));
-               ast_data_add_int(data_hint, "watchers", watchers);
-
-               if (!ast_data_search_match(search, data_hint)) {
-                       ast_data_remove_node(data_root, data_hint);
-               }
-       }
-       ao2_iterator_destroy(&i);
-
-       return 0;
-}
-
-static const struct ast_data_handler hints_data_provider = {
-       .version = AST_DATA_HANDLER_VERSION,
-       .get = hints_data_provider_get
-};
-
-static const struct ast_data_entry pbx_data_providers[] = {
-       AST_DATA_ENTRY("asterisk/core/hints", &hints_data_provider),
-};
-
 static int action_extensionstatelist(struct mansession *s, const struct message *m)
 {
        const char *action_id = astman_get_header(m, "ActionID");
@@ -8230,7 +8393,6 @@ static void unload_pbx(void)
        ast_cli_unregister_multiple(pbx_cli, ARRAY_LEN(pbx_cli));
        ast_custom_function_unregister(&exception_function);
        ast_custom_function_unregister(&testtime_function);
-       ast_data_unregister(NULL);
 }
 
 int load_pbx(void)
@@ -8244,7 +8406,6 @@ int load_pbx(void)
 
        ast_verb(2, "Registering builtin functions:\n");
        ast_cli_register_multiple(pbx_cli, ARRAY_LEN(pbx_cli));
-       ast_data_register_multiple_core(pbx_data_providers, ARRAY_LEN(pbx_data_providers));
        __ast_custom_function_register(&exception_function, NULL);
        __ast_custom_function_register(&testtime_function, NULL);
 
@@ -8318,7 +8479,7 @@ struct ast_context *ast_get_extension_context(struct ast_exten *exten)
 
 const char *ast_get_extension_name(struct ast_exten *exten)
 {
-       return exten ? exten->exten : NULL;
+       return exten ? exten->name : NULL;
 }
 
 const char *ast_get_extension_label(struct ast_exten *exten)
@@ -8344,6 +8505,16 @@ const char *ast_get_extension_registrar(struct ast_exten *e)
        return e ? e->registrar : NULL;
 }
 
+const char *ast_get_extension_registrar_file(struct ast_exten *e)
+{
+       return e ? e->registrar_file : NULL;
+}
+
+int ast_get_extension_registrar_line(struct ast_exten *e)
+{
+       return e ? e->registrar_line : 0;
+}
+
 int ast_get_extension_matchcid(struct ast_exten *e)
 {
        return e ? e->matchcid : 0;
@@ -8351,7 +8522,7 @@ int ast_get_extension_matchcid(struct ast_exten *e)
 
 const char *ast_get_extension_cidmatch(struct ast_exten *e)
 {
-       return e ? e->cidmatch : NULL;
+       return e ? e->cidmatch_display : NULL;
 }
 
 const char *ast_get_extension_app(struct ast_exten *e)
@@ -8364,26 +8535,6 @@ void *ast_get_extension_app_data(struct ast_exten *e)
        return e ? e->data : NULL;
 }
 
-const char *ast_get_switch_name(struct ast_sw *sw)
-{
-       return sw ? sw->name : NULL;
-}
-
-const char *ast_get_switch_data(struct ast_sw *sw)
-{
-       return sw ? sw->data : NULL;
-}
-
-int ast_get_switch_eval(struct ast_sw *sw)
-{
-       return sw->eval;
-}
-
-const char *ast_get_switch_registrar(struct ast_sw *sw)
-{
-       return sw ? sw->registrar : NULL;
-}
-
 /*
  * Walking functions ...
  */
@@ -8401,13 +8552,43 @@ struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
                return exten->next;
 }
 
-struct ast_sw *ast_walk_context_switches(struct ast_context *con,
-       struct ast_sw *sw)
+const struct ast_sw *ast_walk_context_switches(const struct ast_context *con,
+       const struct ast_sw *sw)
 {
-       if (!sw)
-               return con ? AST_LIST_FIRST(&con->alts) : NULL;
-       else
-               return AST_LIST_NEXT(sw, list);
+       if (sw) {
+               int idx;
+               int next = 0;
+
+               for (idx = 0; idx < ast_context_switches_count(con); idx++) {
+                       const struct ast_sw *s = ast_context_switches_get(con, idx);
+
+                       if (next) {
+                               return s;
+                       }
+
+                       if (sw == s) {
+                               next = 1;
+                       }
+               }
+
+               return NULL;
+       }
+
+       if (!ast_context_switches_count(con)) {
+               return NULL;
+       }
+
+       return ast_context_switches_get(con, 0);
+}
+
+int ast_context_switches_count(const struct ast_context *con)
+{
+       return AST_VECTOR_SIZE(&con->alts);
+}
+
+const struct ast_sw *ast_context_switches_get(const struct ast_context *con, int idx)
+{
+       return AST_VECTOR_GET(&con->alts, idx);
 }
 
 struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,