loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / pbx.c
index 37a096f..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,38 +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_include: include= support in extensions.conf */
-struct ast_include {
-       const char *name;
-       const char *rname;                      /*!< Context to include */
-       const char *registrar;                  /*!< Registrar */
-       int hastime;                            /*!< If time construct exists */
-       struct ast_timing timing;               /*!< time construct */
-       struct ast_include *next;               /*!< Link them together */
-       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 ast_ignorepat: Ignore patterns in dial plan */
-struct ast_ignorepat {
-       const char *registrar;
-       struct ast_ignorepat *next;
-       const char pattern[0];
-};
-
 /*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
 struct match_char
 {
@@ -306,40 +280,23 @@ struct scoreboard  /* make sure all fields are 0 before calling new_find_extensi
        struct ast_exten *exten;
 };
 
-/*! \brief ast_context: An extension context */
+/*! \brief ast_context: An extension context - must remain in sync with fake_context */
 struct ast_context {
        ast_rwlock_t lock;                      /*!< A lock to prevent multiple threads from clobbering the context */
        struct ast_exten *root;                 /*!< The root of the list of extensions */
        struct ast_hashtab *root_table;            /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree  */
        struct match_char *pattern_tree;        /*!< A tree to speed up extension pattern matching */
        struct ast_context *next;               /*!< Link them together */
-       struct ast_include *includes;           /*!< Include other contexts */
-       struct ast_ignorepat *ignorepats;       /*!< Patterns for which to continue playing dialtone */
+       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 */
-       AST_LIST_HEAD_NOLOCK(, ast_sw) alts;    /*!< Alternative switches */
+       int autohints;                  /*!< Whether autohints support is enabled or not */
        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 */
 };
 
-/*! \brief ast_app: A registered application */
-struct ast_app {
-       int (*execute)(struct ast_channel *chan, const char *data);
-       AST_DECLARE_STRING_FIELDS(
-               AST_STRING_FIELD(synopsis);     /*!< Synopsis text for 'show applications' */
-               AST_STRING_FIELD(description);  /*!< Description (help text) for 'show application &lt;name&gt;' */
-               AST_STRING_FIELD(syntax);       /*!< Syntax text for 'core show applications' */
-               AST_STRING_FIELD(arguments);    /*!< Arguments description */
-               AST_STRING_FIELD(seealso);      /*!< See also */
-       );
-#ifdef AST_XML_DOCS
-       enum ast_doc_src docsrc;                /*!< Where the documentation come from. */
-#endif
-       AST_RWLIST_ENTRY(ast_app) list;         /*!< Next app in list */
-       struct ast_module *module;              /*!< Module this app belongs to */
-       char name[0];                           /*!< Name of the application */
-};
-
 /*! \brief ast_state_cb: An extension state notify register item */
 struct ast_state_cb {
        /*! Watcher ID returned when registered. */
@@ -388,6 +345,8 @@ struct ast_hint {
        AST_VECTOR(, char *) devices; /*!< Devices associated with the hint */
 };
 
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(hint_change_message_type);
+
 #define HINTDEVICE_DATA_LENGTH 16
 AST_THREADSTORAGE(hintdevice_data);
 
@@ -416,6 +375,19 @@ struct ast_hintdevice {
        char hintdevice[1];
 };
 
+/*! \brief Container for autohint contexts */
+static struct ao2_container *autohints;
+
+/*!
+ * \brief Structure for dial plan autohints
+ */
+struct ast_autohint {
+       /*! \brief Name of the registrar */
+       char *registrar;
+       /*! \brief Name of the context */
+       char context[1];
+};
+
 /*!
  * \note Using the device for hash
  */
@@ -439,6 +411,7 @@ static int hintdevice_hash_cb(const void *obj, const int flags)
 
        return ast_str_case_hash(key);
 }
+
 /*!
  * \note Devices on hints are not unique so no CMP_STOP is returned
  * Dont use ao2_find against hintdevices container cause there always
@@ -473,6 +446,59 @@ static int hintdevice_cmp_multiple(void *obj, void *arg, int flags)
        return cmp ? 0 : CMP_MATCH;
 }
 
+/*!
+ * \note Using the context name for hash
+ */
+static int autohint_hash_cb(const void *obj, const int flags)
+{
+       const struct ast_autohint *autohint;
+       const char *key;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               autohint = obj;
+               key = autohint->context;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
+
+       return ast_str_case_hash(key);
+}
+
+static int autohint_cmp(void *obj, void *arg, int flags)
+{
+       struct ast_autohint *left = obj;
+       struct ast_autohint *right = arg;
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = right->context;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcasecmp(left->context, right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               /*
+               * We could also use a partial key struct containing a length
+               * so strlen() does not get called for every comparison instead.
+               */
+               cmp = strncmp(left->context, right_key, strlen(right_key));
+               break;
+       default:
+               ast_assert(0);
+               cmp = 0;
+               break;
+       }
+       return cmp ? 0 : CMP_MATCH | CMP_STOP;
+}
+
 /*! \internal \brief \c ao2_callback function to remove hintdevices */
 static int hintdevice_remove_cb(void *obj, void *arg, void *data, int flags)
 {
@@ -626,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
@@ -760,15 +788,6 @@ AST_MUTEX_DEFINE_STATIC(conlock);
  */
 AST_MUTEX_DEFINE_STATIC(context_merge_lock);
 
-/*!
- * \brief Registered applications container.
- *
- * It is sorted by application name.
- */
-static AST_RWLIST_HEAD_STATIC(apps, ast_app);
-
-static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
-
 static int stateid = 1;
 /*!
  * \note When holding this container's lock, do _not_ do
@@ -863,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();
                                }
@@ -958,94 +981,6 @@ int check_contexts(char *file, int line )
 }
 #endif
 
-/*
-   \note This function is special. It saves the stack so that no matter
-   how many times it is called, it returns to the same place */
-int pbx_exec(struct ast_channel *c,    /*!< Channel */
-            struct ast_app *app,       /*!< Application */
-            const char *data)          /*!< Data for execution */
-{
-       int res;
-       struct ast_module_user *u = NULL;
-       const char *saved_c_appl;
-       const char *saved_c_data;
-
-       /* save channel values */
-       saved_c_appl= ast_channel_appl(c);
-       saved_c_data= ast_channel_data(c);
-
-       ast_channel_lock(c);
-       ast_channel_appl_set(c, app->name);
-       ast_channel_data_set(c, data);
-       ast_channel_publish_snapshot(c);
-       ast_channel_unlock(c);
-
-       if (app->module)
-               u = __ast_module_user_add(app->module, c);
-       res = app->execute(c, S_OR(data, ""));
-       if (app->module && u)
-               __ast_module_user_remove(app->module, u);
-       /* restore channel values */
-       ast_channel_appl_set(c, saved_c_appl);
-       ast_channel_data_set(c, saved_c_data);
-       return res;
-}
-
-static struct ast_app *pbx_findapp_nolock(const char *name)
-{
-       struct ast_app *cur;
-       int cmp;
-
-       AST_RWLIST_TRAVERSE(&apps, cur, list) {
-               cmp = strcasecmp(name, cur->name);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found it. */
-                       break;
-               }
-               /* Not in container. */
-               cur = NULL;
-               break;
-       }
-
-       return cur;
-}
-
-struct ast_app *pbx_findapp(const char *app)
-{
-       struct ast_app *ret;
-
-       AST_RWLIST_RDLOCK(&apps);
-       ret = pbx_findapp_nolock(app);
-       AST_RWLIST_UNLOCK(&apps);
-
-       return ret;
-}
-
-static struct ast_switch *pbx_findswitch(const char *sw)
-{
-       struct ast_switch *asw;
-
-       AST_RWLIST_RDLOCK(&switches);
-       AST_RWLIST_TRAVERSE(&switches, asw, list) {
-               if (!strcasecmp(asw->name, sw))
-                       break;
-       }
-       AST_RWLIST_UNLOCK(&switches);
-
-       return asw;
-}
-
-static inline int include_valid(struct ast_include *i)
-{
-       if (!i->hastime)
-               return 1;
-
-       return ast_check_timing(&(i->timing));
-}
-
 static void pbx_destroy(struct ast_pbx *p)
 {
        ast_free(p);
@@ -1186,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);
@@ -1305,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 */           \
                                                                }                                                                                                        \
                                                        }                                                                                                            \
@@ -1318,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 */                                                 \
                                                        }                                                                                                                                                \
@@ -1362,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 */
                                        }
                                }
@@ -1384,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 */
                                        }
                                }
@@ -1400,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 */
                                        }
                                }
@@ -1753,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;
@@ -1779,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;
@@ -2186,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;
@@ -2428,6 +2406,7 @@ int ast_extension_close(const char *pattern, const char *data, int needmore)
        return extension_match_core(pattern, data, needmore);
 }
 
+/* This structure must remain in sync with ast_context for proper hashtab matching */
 struct fake_context /* this struct is purely for matching in the hashtab */
 {
        ast_rwlock_t lock;
@@ -2435,11 +2414,12 @@ struct fake_context /* this struct is purely for matching in the hashtab */
        struct ast_hashtab *root_table;
        struct match_char *pattern_tree;
        struct ast_context *next;
-       struct ast_include *includes;
-       struct ast_ignorepat *ignorepats;
+       struct ast_includes includes;
+       struct ast_ignorepats ignorepats;
+       struct ast_sws alts;
        const char *registrar;
        int refcount;
-       AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
+       int autohints;
        ast_mutex_t macrolock;
        char name[256];
 };
@@ -2494,11 +2474,10 @@ 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_include *i = NULL;
-       struct ast_sw *sw = NULL;
        struct ast_exten pattern = {NULL, };
        struct scoreboard score = {0, };
        struct ast_str *tmpdata = NULL;
+       int idx;
 
        pattern.label = label;
        pattern.priority = priority;
@@ -2718,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 */
@@ -2744,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 {
@@ -2764,9 +2747,11 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
        }
        q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
        /* Now try any includes we have in this context */
-       for (i = tmp->includes; i; i = i->next) {
+       for (idx = 0; idx < ast_context_includes_count(tmp); idx++) {
+               const struct ast_include *i = ast_context_includes_get(tmp, idx);
+
                if (include_valid(i)) {
-                       if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) {
+                       if ((e = pbx_find_extension(chan, bypass, q, include_rname(i), exten, priority, label, callerid, action))) {
 #ifdef NEED_DEBUG_HERE
                                ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten);
 #endif
@@ -2927,11 +2912,11 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
                        if (substitute) {
                                pbx_substitute_variables_helper(c, substitute, passdata, sizeof(passdata)-1);
                        }
-                       ast_debug(1, "Launching '%s'\n", app->name);
+                       ast_debug(1, "Launching '%s'\n", app_name(app));
                        if (VERBOSITY_ATLEAST(3)) {
                                ast_verb(3, "Executing [%s@%s:%d] " COLORIZE_FMT "(\"" COLORIZE_FMT "\", \"" COLORIZE_FMT "\") %s\n",
                                        exten, context, priority,
-                                       COLORIZE(COLOR_BRCYAN, 0, app->name),
+                                       COLORIZE(COLOR_BRCYAN, 0, app_name(app)),
                                        COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(c)),
                                        COLORIZE(COLOR_BRMAGENTA, 0, passdata),
                                        "in new stack");
@@ -3266,8 +3251,7 @@ static int execute_state_callback(ast_state_cb_type cb,
                info.exten_state = AST_EXTENSION_REMOVED;
        }
 
-       /* NOTE: The casts will not be needed for v10 and later */
-       res = cb((char *) context, (char *) exten, &info, data);
+       res = cb(context, exten, &info, data);
 
        return res;
 }
@@ -3353,17 +3337,236 @@ static void get_device_state_causing_channels(struct ao2_container *c)
        ao2_iterator_destroy(&iter);
 }
 
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
+{
+       struct ao2_iterator cb_iter;
+       struct ast_state_cb *state_cb;
+       int state;
+       int same_state;
+       struct ao2_container *device_state_info;
+       int first_extended_cb_call = 1;
+       char context_name[AST_MAX_CONTEXT];
+       char exten_name[AST_MAX_EXTENSION];
+
+       ao2_lock(hint);
+       if (!hint->exten) {
+               /* The extension has already been destroyed */
+               ao2_unlock(hint);
+               return;
+       }
+
+       /*
+        * Save off strings in case the hint extension gets destroyed
+        * while we are notifying the watchers.
+        */
+       ast_copy_string(context_name,
+                       ast_get_context_name(ast_get_extension_context(hint->exten)),
+                       sizeof(context_name));
+       ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+                       sizeof(exten_name));
+       ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+       ao2_unlock(hint);
+
+       /*
+        * Get device state for this hint.
+        *
+        * NOTE: We cannot hold any locks while determining the hint
+        * device state or notifying the watchers without causing a
+        * deadlock.  (conlock, hints, and hint)
+        */
+
+       /* Make a container so state3 can fill it if we wish.
+        * If that failed we simply do not provide the extended state info.
+        */
+       device_state_info = alloc_device_state_info();
+
+       state = ast_extension_state3(*hint_app, device_state_info);
+       same_state = state == hint->laststate;
+       if (same_state && (~state & AST_EXTENSION_RINGING)) {
+               ao2_cleanup(device_state_info);
+               return;
+       }
+
+       /* Device state changed since last check - notify the watchers. */
+       hint->laststate = state;        /* record we saw the change */
+
+       /* For general callbacks */
+       if (!same_state) {
+               cb_iter = ao2_iterator_init(statecbs, 0);
+               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+                       execute_state_callback(state_cb->change_cb,
+                               context_name,
+                               exten_name,
+                               state_cb->data,
+                               AST_HINT_UPDATE_DEVICE,
+                               hint,
+                               NULL);
+               }
+               ao2_iterator_destroy(&cb_iter);
+       }
+
+       /* For extension callbacks */
+       /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
+        * included. Normal callbacks are only called when the state changed.
+        */
+       cb_iter = ao2_iterator_init(hint->callbacks, 0);
+       for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+               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);
+               }
+               if (state_cb->extended || !same_state) {
+                       execute_state_callback(state_cb->change_cb,
+                               context_name,
+                               exten_name,
+                               state_cb->data,
+                               AST_HINT_UPDATE_DEVICE,
+                               hint,
+                               state_cb->extended ? device_state_info : NULL);
+               }
+       }
+       ao2_iterator_destroy(&cb_iter);
+
+       ao2_cleanup(device_state_info);
+}
+
+static void presence_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app,
+                                           struct ast_presence_state_message *presence_state)
+{
+       struct ao2_iterator cb_iter;
+       struct ast_state_cb *state_cb;
+       char context_name[AST_MAX_CONTEXT];
+       char exten_name[AST_MAX_EXTENSION];
+
+       ao2_lock(hint);
+       if (!hint->exten) {
+               /* The extension has already been destroyed */
+               ao2_unlock(hint);
+               return;
+       }
+
+       /*
+        * Save off strings in case the hint extension gets destroyed
+        * while we are notifying the watchers.
+        */
+       ast_copy_string(context_name,
+                       ast_get_context_name(ast_get_extension_context(hint->exten)),
+                       sizeof(context_name));
+       ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+                       sizeof(exten_name));
+       ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+       ao2_unlock(hint);
+
+       /* Check to see if update is necessary */
+       if ((hint->last_presence_state == presence_state->state) &&
+           ((hint->last_presence_subtype && presence_state->subtype &&
+             !strcmp(hint->last_presence_subtype, presence_state->subtype)) ||
+            (!hint->last_presence_subtype && !presence_state->subtype)) &&
+           ((hint->last_presence_message && presence_state->message &&
+             !strcmp(hint->last_presence_message, presence_state->message)) ||
+            (!hint->last_presence_message && !presence_state->message))) {
+               /* this update is the same as the last, do nothing */
+               return;
+       }
+
+       /* update new values */
+       ast_free(hint->last_presence_subtype);
+       ast_free(hint->last_presence_message);
+       hint->last_presence_state = presence_state->state;
+       hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
+       hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
+
+       /* For general callbacks */
+       cb_iter = ao2_iterator_init(statecbs, 0);
+       for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+               execute_state_callback(state_cb->change_cb,
+                       context_name,
+                       exten_name,
+                       state_cb->data,
+                       AST_HINT_UPDATE_PRESENCE,
+                       hint,
+                       NULL);
+       }
+       ao2_iterator_destroy(&cb_iter);
+
+       /* For extension callbacks */
+       cb_iter = ao2_iterator_init(hint->callbacks, 0);
+       for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
+               execute_state_callback(state_cb->change_cb,
+                       context_name,
+                       exten_name,
+                       state_cb->data,
+                       AST_HINT_UPDATE_PRESENCE,
+                       hint,
+                       NULL);
+       }
+       ao2_iterator_destroy(&cb_iter);
+}
+
+static int handle_hint_change_message_type(struct stasis_message *msg, enum ast_state_cb_update_reason reason)
+{
+       struct ast_hint *hint;
+       struct ast_str *hint_app;
+
+       if (hint_change_message_type() != stasis_message_type(msg)) {
+               return 0;
+       }
+
+       if (!(hint_app = ast_str_create(1024))) {
+               return -1;
+       }
+
+       hint = stasis_message_data(msg);
+
+       switch (reason) {
+       case AST_HINT_UPDATE_DEVICE:
+               device_state_notify_callbacks(hint, &hint_app);
+               break;
+       case AST_HINT_UPDATE_PRESENCE:
+               {
+                       char *presence_subtype = NULL;
+                       char *presence_message = NULL;
+                       int state;
+
+                       state = extension_presence_state_helper(
+                               hint->exten, &presence_subtype, &presence_message);
+                       {
+                               struct ast_presence_state_message presence_state = {
+                                       .state = state > 0 ? state : AST_PRESENCE_INVALID,
+                                       .subtype = presence_subtype,
+                                       .message = presence_message
+                               };
+
+                               presence_state_notify_callbacks(hint, &hint_app, &presence_state);
+                       }
+
+                       ast_free(presence_subtype);
+                       ast_free(presence_message);
+               }
+               break;
+       }
+
+       ast_free(hint_app);
+       return 1;
+}
+
 static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
        struct ast_device_state_message *dev_state;
-       struct ast_hint *hint;
        struct ast_str *hint_app;
        struct ast_hintdevice *device;
        struct ast_hintdevice *cmpdevice;
        struct ao2_iterator *dev_iter;
-       struct ao2_iterator cb_iter;
-       char context_name[AST_MAX_CONTEXT];
-       char exten_name[AST_MAX_EXTENSION];
+       struct ao2_iterator auto_iter;
+       struct ast_autohint *autohint;
+       char *virtual_device;
+       char *type;
+       char *device_name;
+
+       if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
+               return;
+       }
 
        if (ast_device_state_message_type() != stasis_message_type(msg)) {
                return;
@@ -3375,7 +3578,7 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
                return;
        }
 
-       if (ao2_container_count(hintdevices) == 0) {
+       if (ao2_container_count(hintdevices) == 0 && ao2_container_count(autohints) == 0) {
                /* There are no hints monitoring devices. */
                return;
        }
@@ -3389,110 +3592,61 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
        strcpy(cmpdevice->hintdevice, dev_state->device);
 
        ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+
+       /* Initially we find all hints for the device and notify them */
        dev_iter = ao2_t_callback(hintdevices,
                OBJ_SEARCH_OBJECT | OBJ_MULTIPLE,
                hintdevice_cmp_multiple,
                cmpdevice,
                "find devices in container");
-       if (!dev_iter) {
-               ast_mutex_unlock(&context_merge_lock);
-               ast_free(hint_app);
-               return;
+       if (dev_iter) {
+               for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
+                       if (device->hint) {
+                               device_state_notify_callbacks(device->hint, &hint_app);
+                       }
+               }
+               ao2_iterator_destroy(dev_iter);
        }
 
-       for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
-               struct ast_state_cb *state_cb;
-               int state;
-               int same_state;
-               struct ao2_container *device_state_info;
-               int first_extended_cb_call = 1;
+       /* Second stage we look for any autohint contexts and if the device is not already in the hints
+        * we create it.
+        */
+       type = ast_strdupa(dev_state->device);
+       if (ast_strlen_zero(type)) {
+               goto end;
+       }
+
+       /* Determine if this is a virtual/custom device or a real device */
+       virtual_device = strchr(type, ':');
+       device_name = strchr(type, '/');
+       if (virtual_device && (!device_name || (virtual_device < device_name))) {
+               device_name = virtual_device;
+       }
 
-               if (!device->hint) {
-                       /* Should never happen. */
+       /* Invalid device state name - not a virtual/custom device and not a real device */
+       if (ast_strlen_zero(device_name)) {
+               goto end;
+       }
+
+       *device_name++ = '\0';
+
+       auto_iter = ao2_iterator_init(autohints, 0);
+       for (; (autohint = ao2_iterator_next(&auto_iter)); ao2_t_ref(autohint, -1, "Next autohint")) {
+               if (ast_get_hint(NULL, 0, NULL, 0, NULL, autohint->context, device_name)) {
                        continue;
                }
-               hint = device->hint;
 
-               ao2_lock(hint);
-               if (!hint->exten) {
-                       /* The extension has already been destroyed */
-                       ao2_unlock(hint);
-                       continue;
-               }
-
-               /*
-                * Save off strings in case the hint extension gets destroyed
-                * while we are notifying the watchers.
-                */
-               ast_copy_string(context_name,
-                       ast_get_context_name(ast_get_extension_context(hint->exten)),
-                       sizeof(context_name));
-               ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
-                       sizeof(exten_name));
-               ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-               ao2_unlock(hint);
-
-               /*
-                * Get device state for this hint.
-                *
-                * NOTE: We cannot hold any locks while determining the hint
-                * device state or notifying the watchers without causing a
-                * deadlock.  (conlock, hints, and hint)
-                */
-               /* Make a container so state3 can fill it if we wish.
-                * If that failed we simply do not provide the extended state info.
-                */
-               device_state_info = alloc_device_state_info();
-               state = ast_extension_state3(hint_app, device_state_info);
-               if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
-                       ao2_cleanup(device_state_info);
-                       continue;
-               }
-
-               /* Device state changed since last check - notify the watchers. */
-               hint->laststate = state;        /* record we saw the change */
+               /* The device has no hint in the context referenced by this autohint so create one */
+               ast_add_extension(autohint->context, 0, device_name,
+                       PRIORITY_HINT, NULL, NULL, dev_state->device,
+                       ast_strdup(dev_state->device), ast_free_ptr, autohint->registrar);
 
-               /* For general callbacks */
-               cb_iter = ao2_iterator_init(statecbs, 0);
-               for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-                       execute_state_callback(state_cb->change_cb,
-                               context_name,
-                               exten_name,
-                               state_cb->data,
-                               AST_HINT_UPDATE_DEVICE,
-                               hint,
-                               NULL);
-               }
-               ao2_iterator_destroy(&cb_iter);
-
-               /* For extension callbacks */
-               /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
-                * included. Normal callbacks are only called when the state changed.
-                */
-               cb_iter = ao2_iterator_init(hint->callbacks, 0);
-               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-                       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);
-                       }
-                       if (state_cb->extended || !same_state) {
-                               execute_state_callback(state_cb->change_cb,
-                                       context_name,
-                                       exten_name,
-                                       state_cb->data,
-                                       AST_HINT_UPDATE_DEVICE,
-                                       hint,
-                                       state_cb->extended ? device_state_info : NULL);
-                       }
-               }
-               ao2_iterator_destroy(&cb_iter);
-
-               ao2_cleanup(device_state_info);
+               /* Since this hint was just created there are no watchers, so we don't need to notify anyone */
        }
-       ast_mutex_unlock(&context_merge_lock);
+       ao2_iterator_destroy(&auto_iter);
 
-       ao2_iterator_destroy(dev_iter);
+end:
+       ast_mutex_unlock(&context_merge_lock);
        ast_free(hint_app);
        return;
 }
@@ -3606,28 +3760,24 @@ static int extension_state_add_destroy(const char *context, const char *exten,
        return id;
 }
 
-/*! \brief Add watcher for extension states with destructor */
 int ast_extension_state_add_destroy(const char *context, const char *exten,
        ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
 {
        return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 0);
 }
 
-/*! \brief Add watcher for extension states */
 int ast_extension_state_add(const char *context, const char *exten,
        ast_state_cb_type change_cb, void *data)
 {
        return extension_state_add_destroy(context, exten, change_cb, NULL, data, 0);
 }
 
-/*! \brief Add watcher for extended extension states with destructor */
 int ast_extension_state_add_destroy_extended(const char *context, const char *exten,
        ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
 {
        return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 1);
 }
 
-/*! \brief Add watcher for extended extension states */
 int ast_extension_state_add_extended(const char *context, const char *exten,
        ast_state_cb_type change_cb, void *data)
 {
@@ -3649,7 +3799,6 @@ static int find_hint_by_cb_id(void *obj, void *arg, int flags)
        return 0;
 }
 
-/*! \brief  ast_extension_state_del: Remove a watcher from the callback list */
 int ast_extension_state_del(int id, ast_state_cb_type change_cb)
 {
        struct ast_state_cb *p_cur;
@@ -3818,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;
                }
        }
 
@@ -3855,12 +4003,12 @@ static int ast_add_hint(struct ast_exten *e)
                cb_iter = ao2_iterator_init(statecbs, 0);
                for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
                        execute_state_callback(state_cb->change_cb,
-                                       ast_get_context_name(ast_get_extension_context(e)),
-                                       ast_get_extension_name(e),
-                                       state_cb->data,
-                                       AST_HINT_UPDATE_DEVICE,
-                                       hint_new,
-                                       NULL);
+                               ast_get_context_name(ast_get_extension_context(e)),
+                               ast_get_extension_name(e),
+                               state_cb->data,
+                               AST_HINT_UPDATE_DEVICE,
+                               hint_new,
+                               NULL);
                }
                ao2_iterator_destroy(&cb_iter);
        }
@@ -3870,30 +4018,37 @@ static int ast_add_hint(struct ast_exten *e)
        return 0;
 }
 
-/*! \brief Change hint for an extension */
-static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+/*! \brief Publish a hint changed event  */
+static int publish_hint_change(struct ast_hint *hint, 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;
+       struct stasis_message *message;
 
-       if (!oe || !ne) {
+       if (!hint_change_message_type()) {
                return -1;
        }
 
-       hint_app = ast_str_create(1024);
-       if (!hint_app) {
+       /* The message is going to be published to two topics so the hint needs two refs */
+       if (!(message = stasis_message_create(hint_change_message_type(), ao2_bump(hint)))) {
+               ao2_ref(hint, -1);
                return -1;
        }
 
-       ast_mutex_lock(&context_merge_lock); /* Hold off ast_merge_contexts_and_delete and state changes */
+       stasis_publish(ast_device_state_topic_all(), message);
+       stasis_publish(ast_presence_state_topic_all(), message);
+
+       ao2_ref(message, -1);
+
+       return 0;
+}
+
+/*! \brief Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+       struct ast_hint *hint;
+
+       if (!oe || !ne) {
+               return -1;
+       }
 
        ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
 
@@ -3905,7 +4060,6 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        if (!hint) {
                ao2_unlock(hints);
                ast_mutex_unlock(&context_merge_lock);
-               ast_free(hint_app);
                return -1;
        }
 
@@ -3915,25 +4069,6 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        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);
@@ -3942,104 +4077,15 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
                        ast_get_extension_name(ne),
                        ast_get_context_name(ast_get_extension_context(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);
-       }
+       publish_hint_change(hint, ne);
 
        ao2_ref(hint, -1);
 
-       ast_mutex_unlock(&context_merge_lock);
-
-       ast_free(hint_app);
-       ast_free(previous_message);
-       ast_free(previous_subtype);
-
        return 0;
 }
 
-
 /*! \brief Get hint for channel */
 int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
 {
@@ -4167,248 +4213,6 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
        ast_channel_unlock(chan);
 }
 
-/*!
- * \internal
- * \brief Publish a hangup handler related message to \ref stasis
- */
-static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler)
-{
-       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
-
-       blob = ast_json_pack("{s: s, s: s}",
-                       "type", action,
-                       "handler", S_OR(handler, ""));
-       if (!blob) {
-               return;
-       }
-
-       ast_channel_publish_blob(chan, ast_channel_hangup_handler_type(), blob);
-}
-
-int ast_pbx_hangup_handler_run(struct ast_channel *chan)
-{
-       struct ast_hangup_handler_list *handlers;
-       struct ast_hangup_handler *h_handler;
-
-       ast_channel_lock(chan);
-       handlers = ast_channel_hangup_handlers(chan);
-       if (AST_LIST_EMPTY(handlers)) {
-               ast_channel_unlock(chan);
-               return 0;
-       }
-
-       /*
-        * Make sure that the channel is marked as hungup since we are
-        * going to run the hangup handlers on it.
-        */
-       ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
-
-       for (;;) {
-               handlers = ast_channel_hangup_handlers(chan);
-               h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
-               if (!h_handler) {
-                       break;
-               }
-
-               publish_hangup_handler_message("run", chan, h_handler->args);
-               ast_channel_unlock(chan);
-
-               ast_app_exec_sub(NULL, chan, h_handler->args, 1);
-               ast_free(h_handler);
-
-               ast_channel_lock(chan);
-       }
-       ast_channel_unlock(chan);
-       return 1;
-}
-
-void ast_pbx_hangup_handler_init(struct ast_channel *chan)
-{
-       struct ast_hangup_handler_list *handlers;
-
-       handlers = ast_channel_hangup_handlers(chan);
-       AST_LIST_HEAD_INIT_NOLOCK(handlers);
-}
-
-void ast_pbx_hangup_handler_destroy(struct ast_channel *chan)
-{
-       struct ast_hangup_handler_list *handlers;
-       struct ast_hangup_handler *h_handler;
-
-       ast_channel_lock(chan);
-
-       /* Get rid of each of the hangup handlers on the channel */
-       handlers = ast_channel_hangup_handlers(chan);
-       while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) {
-               ast_free(h_handler);
-       }
-
-       ast_channel_unlock(chan);
-}
-
-int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
-{
-       struct ast_hangup_handler_list *handlers;
-       struct ast_hangup_handler *h_handler;
-
-       ast_channel_lock(chan);
-       handlers = ast_channel_hangup_handlers(chan);
-       h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
-       if (h_handler) {
-               publish_hangup_handler_message("pop", chan, h_handler->args);
-       }
-       ast_channel_unlock(chan);
-       if (h_handler) {
-               ast_free(h_handler);
-               return 1;
-       }
-       return 0;
-}
-
-void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
-{
-       struct ast_hangup_handler_list *handlers;
-       struct ast_hangup_handler *h_handler;
-       const char *expanded_handler;
-
-       if (ast_strlen_zero(handler)) {
-               return;
-       }
-
-       expanded_handler = ast_app_expand_sub_args(chan, handler);
-       if (!expanded_handler) {
-               return;
-       }
-       h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler));
-       if (!h_handler) {
-               ast_free((char *) expanded_handler);
-               return;
-       }
-       strcpy(h_handler->args, expanded_handler);/* Safe */
-       ast_free((char *) expanded_handler);
-
-       ast_channel_lock(chan);
-
-       handlers = ast_channel_hangup_handlers(chan);
-       AST_LIST_INSERT_HEAD(handlers, h_handler, node);
-       publish_hangup_handler_message("push", chan, h_handler->args);
-       ast_channel_unlock(chan);
-}
-
-#define HANDLER_FORMAT "%-30s %s\n"
-
-/*!
- * \internal
- * \brief CLI output the hangup handler headers.
- * \since 11.0
- *
- * \param fd CLI file descriptor to use.
- *
- * \return Nothing
- */
-static void ast_pbx_hangup_handler_headers(int fd)
-{
-       ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler");
-}
-
-/*!
- * \internal
- * \brief CLI output the channel hangup handlers.
- * \since 11.0
- *
- * \param fd CLI file descriptor to use.
- * \param chan Channel to show hangup handlers.
- *
- * \return Nothing
- */
-static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan)
-{
-       struct ast_hangup_handler_list *handlers;
-       struct ast_hangup_handler *h_handler;
-       int first = 1;
-
-       ast_channel_lock(chan);
-       handlers = ast_channel_hangup_handlers(chan);
-       AST_LIST_TRAVERSE(handlers, h_handler, node) {
-               ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args);
-               first = 0;
-       }
-       ast_channel_unlock(chan);
-}
-
-/*
- * \brief 'show hanguphandlers <channel>' CLI command implementation function...
- */
-static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_channel *chan;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show hanguphandlers";
-               e->usage =
-                       "Usage: core show hanguphandlers <channel>\n"
-                       "       Show hangup handlers of a specified channel.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
-       }
-
-       if (a->argc < 4) {
-               return CLI_SHOWUSAGE;
-       }
-
-       chan = ast_channel_get_by_name(a->argv[3]);
-       if (!chan) {
-               ast_cli(a->fd, "Channel does not exist.\n");
-               return CLI_FAILURE;
-       }
-
-       ast_pbx_hangup_handler_headers(a->fd);
-       ast_pbx_hangup_handler_show(a->fd, chan);
-
-       ast_channel_unref(chan);
-
-       return CLI_SUCCESS;
-}
-
-/*
- * \brief 'show hanguphandlers all' CLI command implementation function...
- */
-static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_channel_iterator *iter;
-       struct ast_channel *chan;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show hanguphandlers all";
-               e->usage =
-                       "Usage: core show hanguphandlers all\n"
-                       "       Show hangup handlers for all channels.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
-       }
-
-       if (a->argc < 4) {
-               return CLI_SHOWUSAGE;
-       }
-
-       iter = ast_channel_iterator_all_new();
-       if (!iter) {
-               return CLI_FAILURE;
-       }
-
-       ast_pbx_hangup_handler_headers(a->fd);
-       for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) {
-               ast_pbx_hangup_handler_show(a->fd, chan);
-       }
-       ast_channel_iterator_destroy(iter);
-
-       return CLI_SUCCESS;
-}
-
 /*! helper function to set extension and priority */
 void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
 {
@@ -4496,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  */
@@ -4742,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);
 
@@ -5001,24 +4809,24 @@ int ast_context_remove_include(const char *context, const char *include, const c
  */
 int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
 {
-       struct ast_include *i, *pi = NULL;
        int ret = -1;
+       int idx;
 
        ast_wrlock_context(con);
 
        /* find our include */
-       for (i = con->includes; i; pi = i, i = i->next) {
-               if (!strcmp(i->name, include) &&
-                               (!registrar || !strcmp(i->registrar, registrar))) {
+       for (idx = 0; idx < ast_context_includes_count(con); idx++) {
+               struct ast_include *i = AST_VECTOR_GET(&con->includes, idx);
+
+               if (!strcmp(ast_get_include_name(i), include) &&
+                               (!registrar || !strcmp(ast_get_include_registrar(i), registrar))) {
+
                        /* remove from list */
                        ast_verb(3, "Removing inclusion of context '%s' in context '%s; registrar=%s'\n", include, ast_get_context_name(con), registrar);
-                       if (pi)
-                               pi->next = i->next;
-                       else
-                               con->includes = i->next;
+                       AST_VECTOR_REMOVE_ORDERED(&con->includes, idx);
+
                        /* free include and return */
-                       ast_destroy_timing(&(i->timing));
-                       ast_free(i);
+                       include_free(i);
                        ret = 0;
                        break;
                }
@@ -5058,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);
 
@@ -5124,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;
@@ -5139,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) {
@@ -5164,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);
@@ -5179,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 */
@@ -5191,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 {
@@ -5208,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 */
@@ -5222,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) &&
@@ -5314,247 +5144,10 @@ int ast_context_unlockmacro(const char *context)
        return ret;
 }
 
-/*! \brief Dynamically register a new dial plan application */
-int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, const char *), const char *synopsis, const char *description, void *mod)
-{
-       struct ast_app *tmp;
-       struct ast_app *cur;
-       int length;
-#ifdef AST_XML_DOCS
-       char *tmpxml;
-#endif
-
-       AST_RWLIST_WRLOCK(&apps);
-       cur = pbx_findapp_nolock(app);
-       if (cur) {
-               ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
-       }
-
-       length = sizeof(*tmp) + strlen(app) + 1;
-
-       if (!(tmp = ast_calloc(1, length))) {
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
-       }
-
-       if (ast_string_field_init(tmp, 128)) {
-               AST_RWLIST_UNLOCK(&apps);
-               ast_free(tmp);
-               return -1;
-       }
-
-       strcpy(tmp->name, app);
-       tmp->execute = execute;
-       tmp->module = mod;
-
-#ifdef AST_XML_DOCS
-       /* Try to lookup the docs in our XML documentation database */
-       if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
-               /* load synopsis */
-               tmpxml = ast_xmldoc_build_synopsis("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, synopsis, tmpxml);
-               ast_free(tmpxml);
-
-               /* load description */
-               tmpxml = ast_xmldoc_build_description("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, description, tmpxml);
-               ast_free(tmpxml);
-
-               /* load syntax */
-               tmpxml = ast_xmldoc_build_syntax("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, syntax, tmpxml);
-               ast_free(tmpxml);
-
-               /* load arguments */
-               tmpxml = ast_xmldoc_build_arguments("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, arguments, tmpxml);
-               ast_free(tmpxml);
-
-               /* load seealso */
-               tmpxml = ast_xmldoc_build_seealso("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, seealso, tmpxml);
-               ast_free(tmpxml);
-               tmp->docsrc = AST_XML_DOC;
-       } else {
-#endif
-               ast_string_field_set(tmp, synopsis, synopsis);
-               ast_string_field_set(tmp, description, description);
-#ifdef AST_XML_DOCS
-               tmp->docsrc = AST_STATIC_DOC;
-       }
-#endif
-
-       /* Store in alphabetical order */
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
-               if (strcasecmp(tmp->name, cur->name) < 0) {
-                       AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
-                       break;
-               }
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       if (!cur)
-               AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
-
-       ast_verb(2, "Registered application '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, tmp->name));
-
-       AST_RWLIST_UNLOCK(&apps);
-
-       return 0;
-}
-
-/*
- * Append to the list. We don't have a tail pointer because we need
- * to scan the list anyways to check for duplicates during insertion.
- */
-int ast_register_switch(struct ast_switch *sw)
-{
-       struct ast_switch *tmp;
-
-       AST_RWLIST_WRLOCK(&switches);
-       AST_RWLIST_TRAVERSE(&switches, tmp, list) {
-               if (!strcasecmp(tmp->name, sw->name)) {
-                       AST_RWLIST_UNLOCK(&switches);
-                       ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
-                       return -1;
-               }
-       }
-       AST_RWLIST_INSERT_TAIL(&switches, sw, list);
-       AST_RWLIST_UNLOCK(&switches);
-
-       return 0;
-}
-
-void ast_unregister_switch(struct ast_switch *sw)
-{
-       AST_RWLIST_WRLOCK(&switches);
-       AST_RWLIST_REMOVE(&switches, sw, list);
-       AST_RWLIST_UNLOCK(&switches);
-}
-
 /*
  * Help for CLI commands ...
  */
 
-static void print_app_docs(struct ast_app *aa, int fd)
-{
-#ifdef AST_XML_DOCS
-       char *synopsis = NULL, *description = NULL, *arguments = NULL, *seealso = NULL;
-       if (aa->docsrc == AST_XML_DOC) {
-               synopsis = ast_xmldoc_printable(S_OR(aa->synopsis, "Not available"), 1);
-               description = ast_xmldoc_printable(S_OR(aa->description, "Not available"), 1);
-               arguments = ast_xmldoc_printable(S_OR(aa->arguments, "Not available"), 1);
-               seealso = ast_xmldoc_printable(S_OR(aa->seealso, "Not available"), 1);
-               if (!synopsis || !description || !arguments || !seealso) {
-                       goto free_docs;
-               }
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s%s%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                               ast_term_color(COLOR_CYAN, 0), S_OR(aa->syntax, "Not available"), ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso);
-free_docs:
-               ast_free(synopsis);
-               ast_free(description);
-               ast_free(arguments);
-               ast_free(seealso);
-       } else
-#endif
-       {
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->synopsis, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->description, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->syntax, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->arguments, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->seealso, "Not available")));
-       }
-}
-
-/*
- * \brief 'show application' CLI command implementation function...
- */
-static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_app *aa;
-       int app, no_registered_app = 1;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show application";
-               e->usage =
-                       "Usage: core show application <application> [<application> [<application> [...]]]\n"
-                       "       Describes a particular application.\n";
-               return NULL;
-       case CLI_GENERATE:
-               /*
-                * There is a possibility to show informations about more than one
-                * application at one time. You can type 'show application Dial Echo' and
-                * you will see informations about these two applications ...
-                */
-               return ast_complete_applications(a->line, a->word, a->n);
-       }
-
-       if (a->argc < 4) {
-               return CLI_SHOWUSAGE;
-       }
-
-       AST_RWLIST_RDLOCK(&apps);
-       AST_RWLIST_TRAVERSE(&apps, aa, list) {
-               /* Check for each app that was supplied as an argument */
-               for (app = 3; app < a->argc; app++) {
-                       if (strcasecmp(aa->name, a->argv[app])) {
-                               continue;
-                       }
-
-                       /* We found it! */
-                       no_registered_app = 0;
-
-                       print_app_docs(aa, a->fd);
-               }
-       }
-       AST_RWLIST_UNLOCK(&apps);
-
-       /* we found at least one app? no? */
-       if (no_registered_app) {
-               ast_cli(a->fd, "Your application(s) is (are) not registered\n");
-               return CLI_FAILURE;
-       }
-
-       return CLI_SUCCESS;
-}
-
 /*! \brief  handle_show_hints: CLI support for listing registered dial plan hints */
 static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -5720,40 +5313,6 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        return CLI_SUCCESS;
 }
 
-
-/*! \brief  handle_show_switches: CLI support for listing registered dial plan switches */
-static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_switch *sw;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show switches";
-               e->usage =
-                       "Usage: core show switches\n"
-                       "       List registered switches\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       AST_RWLIST_RDLOCK(&switches);
-
-       if (AST_RWLIST_EMPTY(&switches)) {
-               AST_RWLIST_UNLOCK(&switches);
-               ast_cli(a->fd, "There are no registered alternative switches\n");
-               return CLI_SUCCESS;
-       }
-
-       ast_cli(a->fd, "\n    -= Registered Asterisk Alternative Switches =-\n");
-       AST_RWLIST_TRAVERSE(&switches, sw, list)
-               ast_cli(a->fd, "%s: %s\n", sw->name, sw->description);
-
-       AST_RWLIST_UNLOCK(&switches);
-
-       return CLI_SUCCESS;
-}
-
 #if 0
 /* This code can be used to test if the system survives running out of memory.
  * It might be an idea to put this in only if ENABLE_AUTODESTRUCT_TESTS is enabled.
@@ -5819,98 +5378,15 @@ static char *handle_eat_memory(struct ast_cli_entry *e, int cmd, struct ast_cli_
                        }
 
                        blocks[blocks_pos++] = block;
-                       allocated += alloc_size;
-               }
-       }
-
-       /* No freeing of the mem! */
-       ast_log(LOG_WARNING, "Allocated %lld bytes total!\n", allocated);
-       return CLI_SUCCESS;
-}
-#endif
-
-static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_app *aa;
-       int like = 0, describing = 0;
-       int total_match = 0;    /* Number of matches in like clause */
-       int total_apps = 0;     /* Number of apps registered */
-       static const char * const choices[] = { "like", "describing", NULL };
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show applications [like|describing]";
-               e->usage =
-                       "Usage: core show applications [{like|describing} <text>]\n"
-                       "       List applications which are currently available.\n"
-                       "       If 'like', <text> will be a substring of the app name\n"
-                       "       If 'describing', <text> will be a substring of the description\n";
-               return NULL;
-       case CLI_GENERATE:
-               return (a->pos != 3) ? NULL : ast_cli_complete(a->word, choices, a->n);
-       }
-
-       AST_RWLIST_RDLOCK(&apps);
-
-       if (AST_RWLIST_EMPTY(&apps)) {
-               ast_cli(a->fd, "There are no registered applications\n");
-               AST_RWLIST_UNLOCK(&apps);
-               return CLI_SUCCESS;
-       }
-
-       /* core list applications like <keyword> */
-       if ((a->argc == 5) && (!strcmp(a->argv[3], "like"))) {
-               like = 1;
-       } else if ((a->argc > 4) && (!strcmp(a->argv[3], "describing"))) {
-               describing = 1;
-       }
-
-       /* core list applications describing <keyword1> [<keyword2>] [...] */
-       if ((!like) && (!describing)) {
-               ast_cli(a->fd, "    -= Registered Asterisk Applications =-\n");
-       } else {
-               ast_cli(a->fd, "    -= Matching Asterisk Applications =-\n");
-       }
-
-       AST_RWLIST_TRAVERSE(&apps, aa, list) {
-               int printapp = 0;
-               total_apps++;
-               if (like) {
-                       if (strcasestr(aa->name, a->argv[4])) {
-                               printapp = 1;
-                               total_match++;
-                       }
-               } else if (describing) {
-                       if (aa->description) {
-                               /* Match all words on command line */
-                               int i;
-                               printapp = 1;
-                               for (i = 4; i < a->argc; i++) {
-                                       if (!strcasestr(aa->description, a->argv[i])) {
-                                               printapp = 0;
-                                       } else {
-                                               total_match++;
-                                       }
-                               }
-                       }
-               } else {
-                       printapp = 1;
-               }
-
-               if (printapp) {
-                       ast_cli(a->fd,"  %20s: %s\n", aa->name, aa->synopsis ? aa->synopsis : "<Synopsis not available>");
+                       allocated += alloc_size;
                }
        }
-       if ((!like) && (!describing)) {
-               ast_cli(a->fd, "    -= %d Applications Registered =-\n",total_apps);
-       } else {
-               ast_cli(a->fd, "    -= %d Applications Matching =-\n",total_match);
-       }
-
-       AST_RWLIST_UNLOCK(&apps);
 
+       /* No freeing of the mem! */
+       ast_log(LOG_WARNING, "Allocated %lld bytes total!\n", allocated);
        return CLI_SUCCESS;
 }
+#endif
 
 /*
  * 'show dialplan' CLI command implementation functions ...
@@ -5968,8 +5444,23 @@ 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, struct ast_include *rinclude, int includecount, const char *includes[])
+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[])
 {
        struct ast_context *c = NULL;
        int res = 0, old_total_exten = dpc->total_exten;
@@ -5978,9 +5469,8 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
 
        /* walk all contexts ... */
        while ( (c = ast_walk_contexts(c)) ) {
+               int idx;
                struct ast_exten *e;
-               struct ast_include *i;
-               struct ast_ignorepat *ip;
 #ifndef LOW_MEMORY
                char buf[1024], buf2[1024];
 #else
@@ -6005,6 +5495,9 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
                        dpc->total_context++;
                        ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
                                ast_get_context_name(c), ast_get_context_registrar(c));
+                       if (c->autohints) {
+                               ast_cli(fd, "Autohints support enabled\n");
+                       }
                        context_info_printed = 1;
                }
 
@@ -6027,6 +5520,9 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
                                } else {
                                        ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
                                                ast_get_context_name(c), ast_get_context_registrar(c));
+                                       if (c->autohints) {
+                                               ast_cli(fd, "Autohints support enabled\n");
+                                       }
                                }
                                context_info_printed = 1;
                        }
@@ -6040,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 */
@@ -6055,14 +5550,14 @@ 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);
                        }
                }
 
                /* walk included and write info ... */
-               i = NULL;
-               while ( (i = ast_walk_context_includes(c, i)) ) {
+               for (idx = 0; idx < ast_context_includes_count(c); idx++) {
+                       const struct ast_include *i = ast_context_includes_get(c, idx);
+
                        snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
                        if (exten) {
                                /* Check all includes for the requested extension */
@@ -6091,10 +5586,11 @@ static int show_dialplan_helper(int fd, const char *context, const char *exten,
                }
 
                /* walk ignore patterns and write info ... */
-               ip = NULL;
-               while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
+               for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
+                       const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
                        const char *ipname = ast_get_ignorepat_name(ip);
                        char ignorepat[AST_MAX_EXTENSION];
+
                        snprintf(buf, sizeof(buf), "'%s'", ipname);
                        snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
                        if (!exten || ast_extension_match(ignorepat, exten)) {
@@ -6103,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));
@@ -6314,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,
-                                       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;
@@ -6335,9 +5833,8 @@ static int manager_show_dialplan_helper(struct mansession *s, const struct messa
 
        c = NULL;               /* walk all contexts ... */
        while ( (c = ast_walk_contexts(c)) ) {
+               int idx;
                struct ast_exten *e;
-               struct ast_include *i;
-               struct ast_ignorepat *ip;
 
                if (context && strcmp(ast_get_context_name(c), context) != 0)
                        continue;       /* not the name we want */
@@ -6392,11 +5889,29 @@ static int manager_show_dialplan_helper(struct mansession *s, const struct messa
                        }
                }
 
-               i = NULL;               /* walk included and write info ... */
-               while ( (i = ast_walk_context_includes(c, i)) ) {
+               for (idx = 0; idx < ast_context_includes_count(c); idx++) {
+                       const struct ast_include *i = ast_context_includes_get(c, idx);
+
                        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);
@@ -6407,8 +5922,8 @@ static int manager_show_dialplan_helper(struct mansession *s, const struct messa
                        }
                }
 
-               ip = NULL;      /* walk ignore patterns and write info ... */
-               while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
+               for (idx = 0; idx < ast_context_ignorepats_count(c); idx++) {
+                       const struct ast_ignorepat *ip = ast_context_ignorepats_get(c, idx);
                        const char *ipname = ast_get_ignorepat_name(ip);
                        char ignorepat[AST_MAX_EXTENSION];
 
@@ -6422,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);
@@ -6451,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 */
@@ -6466,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];
@@ -6595,23 +6112,18 @@ static struct ast_cli_entry pbx_cli[] = {
 #if 0
        AST_CLI_DEFINE(handle_eat_memory, "Eats all available memory"),
 #endif
-       AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
-       AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"),
        AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
        AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
 #ifdef AST_DEVMODE
        AST_CLI_DEFINE(handle_show_device2extenstate, "Show expected exten state from multiple device states"),
 #endif
-       AST_CLI_DEFINE(handle_show_hangup_all, "Show hangup handlers of all channels"),
-       AST_CLI_DEFINE(handle_show_hangup_channel, "Show hangup handlers of a specified channel"),
-       AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
        AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),
        AST_CLI_DEFINE(handle_debug_dialplan, "Show fast extension pattern matching data structures"),
        AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),
        AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),
 };
 
-static void unreference_cached_app(struct ast_app *app)
+void unreference_cached_app(struct ast_app *app)
 {
        struct ast_context *context = NULL;
        struct ast_exten *eroot = NULL, *e = NULL;
@@ -6630,36 +6142,6 @@ static void unreference_cached_app(struct ast_app *app)
        return;
 }
 
-int ast_unregister_application(const char *app)
-{
-       struct ast_app *cur;
-       int cmp;
-
-       AST_RWLIST_WRLOCK(&apps);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
-               cmp = strcasecmp(app, cur->name);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found it. */
-                       unreference_cached_app(cur);
-                       AST_RWLIST_REMOVE_CURRENT(list);
-                       ast_verb(2, "Unregistered application '%s'\n", cur->name);
-                       ast_string_field_free_memory(cur);
-                       ast_free(cur);
-                       break;
-               }
-               /* Not in container. */
-               cur = NULL;
-               break;
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       AST_RWLIST_UNLOCK(&apps);
-
-       return cur ? 0 : -1;
-}
-
 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar)
 {
        struct ast_context *tmp, **local_contexts;
@@ -6706,8 +6188,9 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
                tmp->root = NULL;
                tmp->root_table = NULL;
                tmp->registrar = ast_strdup(registrar);
-               tmp->includes = NULL;
-               tmp->ignorepats = NULL;
+               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);
@@ -6734,6 +6217,11 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
        return tmp;
 }
 
+void ast_context_set_autohints(struct ast_context *con, int enabled)
+{
+       con->autohints = enabled;
+}
+
 void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar);
 
 struct store_hint {
@@ -6753,34 +6241,76 @@ 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)
 {
-       struct ast_include *i;
-       struct ast_ignorepat *ip;
-       struct ast_sw *sw;
+       int idx;
 
        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 */
        /* walk through includes */
-       for (i = NULL; (i = ast_walk_context_includes(old, i)) ; ) {
-               if (strcmp(ast_get_include_registrar(i), registrar) == 0)
+       for (idx = 0; idx < ast_context_includes_count(old); idx++) {
+               const struct ast_include *i = ast_context_includes_get(old, idx);
+
+               if (!strcmp(ast_get_include_registrar(i), registrar)) {
                        continue; /* not mine */
+               }
                ast_context_add_include2(new, ast_get_include_name(i), ast_get_include_registrar(i));
        }
 
        /* 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));
        }
 
        /* walk thru ignorepats ... */
-       for (ip = NULL; (ip = ast_walk_context_ignorepats(old, ip)); ) {
-               if (strcmp(ast_get_ignorepat_registrar(ip), registrar) == 0)
+       for (idx = 0; idx < ast_context_ignorepats_count(old); idx++) {
+               const struct ast_ignorepat *ip = ast_context_ignorepats_get(old, idx);
+
+               if (strcmp(ast_get_ignorepat_registrar(ip), registrar) == 0) {
                        continue; /* not mine */
+               }
                ast_context_add_ignorepat2(new, ast_get_ignorepat_name(ip), ast_get_ignorepat_registrar(ip));
        }
 }
 
+/*! Set up an autohint placeholder in the hints container */
+static void context_table_create_autohints(struct ast_hashtab *table)
+{
+       struct ast_context *con;
+       struct ast_hashtab_iter *iter;
+
+       /* Remove all autohints as the below iteration will recreate them */
+       ao2_callback(autohints, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+
+       iter = ast_hashtab_start_traversal(table);
+       while ((con = ast_hashtab_next(iter))) {
+               size_t name_len = strlen(con->name) + 1;
+               size_t registrar_len = strlen(con->registrar) + 1;
+               struct ast_autohint *autohint;
+
+               if (!con->autohints) {
+                       continue;
+               }
+
+               autohint = ao2_alloc_options(sizeof(*autohint) + name_len + registrar_len, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+               if (!autohint) {
+                       continue;
+               }
+
+               ast_copy_string(autohint->context, con->name, name_len);
+               autohint->registrar = autohint->context + name_len;
+               ast_copy_string(autohint->registrar, con->registrar, registrar_len);
+
+               ao2_link(autohints, autohint);
+               ao2_ref(autohint, -1);
+
+               ast_verb(3, "Enabled autohints support on context '%s'\n", con->name);
+       }
+       ast_hashtab_end_traversal(iter);
+}
 
 /* the purpose of this routine is to duplicate a context, with all its substructure,
    except for any extens that have a matching registrar */
@@ -6822,6 +6352,9 @@ static void context_merge(struct ast_context **extcontexts, struct ast_hashtab *
                                /* make sure the new context exists, so we have somewhere to stick this exten/prio */
                                if (!new) {
                                        new = ast_context_find_or_create(extcontexts, exttable, context->name, prio_item->registrar); /* a new context created via priority from a different context in the old dialplan, gets its registrar from the prio's registrar */
+                                       if (new) {
+                                               new->autohints = context->autohints;
+                                       }
                                }
 
                                /* copy in the includes, switches, and ignorepats */
@@ -6841,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! */
@@ -6868,6 +6402,10 @@ static void context_merge(struct ast_context **extcontexts, struct ast_hashtab *
                   but that's not available, so we give it the registrar we know about */
                new = ast_context_find_or_create(extcontexts, exttable, context->name, context->registrar);
 
+               if (new) {
+                       new->autohints = context->autohints;
+               }
+
                /* copy in the includes, switches, and ignorepats */
                context_merge_incls_swits_igps_other_registrars(new, context, registrar);
        }
@@ -6912,6 +6450,9 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
        ast_wrlock_contexts();
 
        if (!contexts_table) {
+               /* Create any autohint contexts */
+               context_table_create_autohints(exttable);
+
                /* Well, that's odd. There are no contexts. */
                contexts_table = exttable;
                contexts = *extcontexts;
@@ -7034,6 +6575,9 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
                }
        }
 
+       /* Create all applicable autohint contexts */
+       context_table_create_autohints(contexts_table);
+
        ao2_unlock(hints);
        ast_unlock_contexts();
 
@@ -7114,260 +6658,6 @@ int ast_context_add_include(const char *context, const char *include, const char
        return ret;
 }
 
-/*! \brief Helper for get_range.
- * return the index of the matching entry, starting from 1.
- * If names is not supplied, try numeric values.
- */
-static int lookup_name(const char *s, const char * const names[], int max)
-{
-       int i;
-
-       if (names && *s > '9') {
-               for (i = 0; names[i]; i++) {
-                       if (!strcasecmp(s, names[i])) {
-                               return i;
-                       }
-               }
-       }
-
-       /* Allow months and weekdays to be specified as numbers, as well */
-       if (sscanf(s, "%2d", &i) == 1 && i >= 1 && i <= max) {
-               /* What the array offset would have been: "1" would be at offset 0 */
-               return i - 1;
-       }
-       return -1; /* error return */
-}
-
-/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
- * names, if supplied, is an array of names that should be mapped to numbers.
- */
-static unsigned get_range(char *src, int max, const char * const names[], const char *msg)
-{
-       int start, end; /* start and ending position */
-       unsigned int mask = 0;
-       char *part;
-
-       /* Check for whole range */
-       if (ast_strlen_zero(src) || !strcmp(src, "*")) {
-               return (1 << max) - 1;
-       }
-
-       while ((part = strsep(&src, "&"))) {
-               /* Get start and ending position */
-               char *endpart = strchr(part, '-');
-               if (endpart) {
-                       *endpart++ = '\0';
-               }
-               /* Find the start */
-               if ((start = lookup_name(part, names, max)) < 0) {
-                       ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
-                       continue;
-               }
-               if (endpart) { /* find end of range */
-                       if ((end = lookup_name(endpart, names, max)) < 0) {
-                               ast_log(LOG_WARNING, "Invalid end %s '%s', skipping element\n", msg, endpart);
-                               continue;
-                       }
-               } else {
-                       end = start;
-               }
-               /* Fill the mask. Remember that ranges are cyclic */
-               mask |= (1 << end);   /* initialize with last element */
-               while (start != end) {
-                       mask |= (1 << start);
-                       if (++start >= max) {
-                               start = 0;
-                       }
-               }
-       }
-       return mask;
-}
-
-/*! \brief store a bitmask of valid times, one bit each 1 minute */
-static void get_timerange(struct ast_timing *i, char *times)
-{
-       char *endpart, *part;
-       int x;
-       int st_h, st_m;
-       int endh, endm;
-       int minute_start, minute_end;
-
-       /* start disabling all times, fill the fields with 0's, as they may contain garbage */
-       memset(i->minmask, 0, sizeof(i->minmask));
-
-       /* 1-minute per bit */
-       /* Star is all times */
-       if (ast_strlen_zero(times) || !strcmp(times, "*")) {
-               /* 48, because each hour takes 2 integers; 30 bits each */
-               for (x = 0; x < 48; x++) {
-                       i->minmask[x] = 0x3fffffff; /* 30 bits */
-               }
-               return;
-       }
-       /* Otherwise expect a range */
-       while ((part = strsep(&times, "&"))) {
-               if (!(endpart = strchr(part, '-'))) {
-                       if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
-                               ast_log(LOG_WARNING, "%s isn't a valid time.\n", part);
-                               continue;
-                       }
-                       i->minmask[st_h * 2 + (st_m >= 30 ? 1 : 0)] |= (1 << (st_m % 30));
-                       continue;
-               }
-               *endpart++ = '\0';
-               /* why skip non digits? Mostly to skip spaces */
-               while (*endpart && !isdigit(*endpart)) {
-                       endpart++;
-               }
-               if (!*endpart) {
-                       ast_log(LOG_WARNING, "Invalid time range starting with '%s-'.\n", part);
-                       continue;
-               }
-               if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
-                       ast_log(LOG_WARNING, "'%s' isn't a valid start time.\n", part);
-                       continue;
-               }
-               if (sscanf(endpart, "%2d:%2d", &endh, &endm) != 2 || endh < 0 || endh > 23 || endm < 0 || endm > 59) {
-                       ast_log(LOG_WARNING, "'%s' isn't a valid end time.\n", endpart);
-                       continue;
-               }
-               minute_start = st_h * 60 + st_m;
-               minute_end = endh * 60 + endm;
-               /* Go through the time and enable each appropriate bit */
-               for (x = minute_start; x != minute_end; x = (x + 1) % (24 * 60)) {
-                       i->minmask[x / 30] |= (1 << (x % 30));
-               }
-               /* Do the last one */
-               i->minmask[x / 30] |= (1 << (x % 30));
-       }
-       /* All done */
-       return;
-}
-
-static const char * const days[] =
-{
-       "sun",
-       "mon",
-       "tue",
-       "wed",
-       "thu",
-       "fri",
-       "sat",
-       NULL,
-};
-
-static const char * const months[] =
-{
-       "jan",
-       "feb",
-       "mar",
-       "apr",
-       "may",
-       "jun",
-       "jul",
-       "aug",
-       "sep",
-       "oct",
-       "nov",
-       "dec",
-       NULL,
-};
-/*! /brief Build timing
- *
- * /param i info
- * /param info_in
- *
- */
-int ast_build_timing(struct ast_timing *i, const char *info_in)
-{
-       char *info;
-       int j, num_fields, last_sep = -1;
-
-       i->timezone = NULL;
-
-       /* Check for empty just in case */
-       if (ast_strlen_zero(info_in)) {
-               return 0;
-       }
-
-       /* make a copy just in case we were passed a static string */
-       info = ast_strdupa(info_in);
-
-       /* count the number of fields in the timespec */
-       for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
-               if (info[j] == ',') {
-                       last_sep = j;
-                       num_fields++;
-               }
-       }
-
-       /* save the timezone, if it is specified */
-       if (num_fields == 5) {
-               i->timezone = ast_strdup(info + last_sep + 1);
-       }
-
-       /* Assume everything except time */
-       i->monthmask = 0xfff;   /* 12 bits */
-       i->daymask = 0x7fffffffU; /* 31 bits */
-       i->dowmask = 0x7f; /* 7 bits */
-       /* on each call, use strsep() to move info to the next argument */
-       get_timerange(i, strsep(&info, "|,"));
-       if (info)
-               i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
-       if (info)
-               i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
-       if (info)
-               i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
-       return 1;
-}
-
-int ast_check_timing(const struct ast_timing *i)
-{
-       return ast_check_timing2(i, ast_tvnow());
-}
-
-int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
-{
-       struct ast_tm tm;
-
-       ast_localtime(&tv, &tm, i->timezone);
-
-       /* If it's not the right month, return */
-       if (!(i->monthmask & (1 << tm.tm_mon)))
-               return 0;
-
-       /* If it's not that time of the month.... */
-       /* Warning, tm_mday has range 1..31! */
-       if (!(i->daymask & (1 << (tm.tm_mday-1))))
-               return 0;
-
-       /* If it's not the right day of the week */
-       if (!(i->dowmask & (1 << tm.tm_wday)))
-               return 0;
-
-       /* Sanity check the hour just to be safe */
-       if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
-               ast_log(LOG_WARNING, "Insane time...\n");
-               return 0;
-       }
-
-       /* Now the tough part, we calculate if it fits
-          in the right time based on min/hour */
-       if (!(i->minmask[tm.tm_hour * 2 + (tm.tm_min >= 30 ? 1 : 0)] & (1 << (tm.tm_min >= 30 ? tm.tm_min - 30 : tm.tm_min))))
-               return 0;
-
-       /* If we got this far, then we're good */
-       return 1;
-}
-
-int ast_destroy_timing(struct ast_timing *i)
-{
-       if (i->timezone) {
-               ast_free(i->timezone);
-               i->timezone = NULL;
-       }
-       return 0;
-}
 /*
  * errno values
  *  ENOMEM - out of memory
@@ -7379,54 +6669,36 @@ int ast_context_add_include2(struct ast_context *con, const char *value,
        const char *registrar)
 {
        struct ast_include *new_include;
-       char *c;
-       struct ast_include *i, *il = NULL; /* include, include_last */
-       int length;
-       char *p;
-
-       length = sizeof(struct ast_include);
-       length += 2 * (strlen(value) + 1);
+       int idx;
 
        /* allocate new include structure ... */
-       if (!(new_include = ast_calloc(1, length)))
+       new_include = include_alloc(value, registrar);
+       if (!new_include) {
                return -1;
-       /* Fill in this structure. Use 'p' for assignments, as the fields
-        * in the structure are 'const char *'
-        */
-       p = new_include->stuff;
-       new_include->name = p;
-       strcpy(p, value);
-       p += strlen(value) + 1;
-       new_include->rname = p;
-       strcpy(p, value);
-       /* Strip off timing info, and process if it is there */
-       if ( (c = strchr(p, ',')) ) {
-               *c++ = '\0';
-               new_include->hastime = ast_build_timing(&(new_include->timing), c);
-       }
-       new_include->next      = NULL;
-       new_include->registrar = registrar;
+       }
 
        ast_wrlock_context(con);
 
        /* ... go to last include and check if context is already included too... */
-       for (i = con->includes; i; i = i->next) {
-               if (!strcasecmp(i->name, new_include->name)) {
-                       ast_destroy_timing(&(new_include->timing));
-                       ast_free(new_include);
+       for (idx = 0; idx < ast_context_includes_count(con); idx++) {
+               const struct ast_include *i = ast_context_includes_get(con, idx);
+
+               if (!strcasecmp(ast_get_include_name(i), ast_get_include_name(new_include))) {
+                       include_free(new_include);
                        ast_unlock_context(con);
                        errno = EEXIST;
                        return -1;
                }
-               il = i;
        }
 
        /* ... include new context into context list, unlock, return */
-       if (il)
-               il->next = new_include;
-       else
-               con->includes = new_include;
-       ast_verb(3, "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+       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));
 
        ast_unlock_context(con);
 
@@ -7461,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;
@@ -7505,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);
 
@@ -7533,24 +6791,20 @@ int ast_context_remove_ignorepat(const char *context, const char *ignorepat, con
 
 int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
 {
-       struct ast_ignorepat *ip, *ipl = NULL;
+       int idx;
 
        ast_wrlock_context(con);
 
-       for (ip = con->ignorepats; ip; ip = ip->next) {
-               if (!strcmp(ip->pattern, ignorepat) &&
-                       (!registrar || (registrar == ip->registrar))) {
-                       if (ipl) {
-                               ipl->next = ip->next;
-                               ast_free(ip);
-                       } else {
-                               con->ignorepats = ip->next;
-                               ast_free(ip);
-                       }
+       for (idx = 0; idx < ast_context_ignorepats_count(con); idx++) {
+               struct ast_ignorepat *ip = AST_VECTOR_GET(&con->ignorepats, idx);
+
+               if (!strcmp(ast_get_ignorepat_name(ip), ignorepat) &&
+                       (!registrar || (registrar == ast_get_ignorepat_registrar(ip)))) {
+                       AST_VECTOR_REMOVE_ORDERED(&con->ignorepats, idx);
+                       ignorepat_free(ip);
                        ast_unlock_context(con);
                        return 0;
                }
-               ipl = ip;
        }
 
        ast_unlock_context(con);
@@ -7577,41 +6831,33 @@ int ast_context_add_ignorepat(const char *context, const char *value, const char
 
 int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
 {
-       struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
-       int length;
-       char *pattern;
-       length = sizeof(struct ast_ignorepat);
-       length += strlen(value) + 1;
-       if (!(ignorepat = ast_calloc(1, length)))
+       struct ast_ignorepat *ignorepat = ignorepat_alloc(value, registrar);
+       int idx;
+
+       if (!ignorepat) {
                return -1;
-       /* The cast to char * is because we need to write the initial value.
-        * The field is not supposed to be modified otherwise.  Also, gcc 4.2
-        * sees the cast as dereferencing a type-punned pointer and warns about
-        * it.  This is the workaround (we're telling gcc, yes, that's really
-        * what we wanted to do).
-        */
-       pattern = (char *) ignorepat->pattern;
-       strcpy(pattern, value);
-       ignorepat->next = NULL;
-       ignorepat->registrar = registrar;
+       }
+
        ast_wrlock_context(con);
-       for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
-               ignorepatl = ignorepatc;
-               if (!strcasecmp(ignorepatc->pattern, value)) {
+       for (idx = 0; idx < ast_context_ignorepats_count(con); idx++) {
+               const struct ast_ignorepat *i = ast_context_ignorepats_get(con, idx);
+
+               if (!strcasecmp(ast_get_ignorepat_name(i), value)) {
                        /* Already there */
                        ast_unlock_context(con);
-                       ast_free(ignorepat);
+                       ignorepat_free(ignorepat);
                        errno = EEXIST;
                        return -1;
                }
        }
-       if (ignorepatl)
-               ignorepatl->next = ignorepat;
-       else
-               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;
 
+       return 0;
 }
 
 int ast_ignore_pattern(const char *context, const char *pattern)
@@ -7622,10 +6868,12 @@ int ast_ignore_pattern(const char *context, const char *pattern)
        ast_rdlock_contexts();
        con = ast_context_find(context);
        if (con) {
-               struct ast_ignorepat *pat;
+               int idx;
 
-               for (pat = con->ignorepats; pat; pat = pat->next) {
-                       if (ast_extension_match(pat->pattern, pattern)) {
+               for (idx = 0; idx < ast_context_ignorepats_count(con); idx++) {
+                       const struct ast_ignorepat *pat = ast_context_ignorepats_get(con, idx);
+
+                       if (ast_extension_match(ast_get_ignorepat_name(pat), pattern)) {
                                ret = 1;
                                break;
                        }
@@ -7651,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;
@@ -7671,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();
        }
 
@@ -7748,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;
+
+       if (!dst_size--) {
+               /* There really is no dst buffer */
+               return 0;
+       }
 
-       while (*src && (count < len - 1)) {
+       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;
 }
 
 /*!
@@ -7787,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;
                }
@@ -7820,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;
@@ -7971,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);
 }
 
 
@@ -7997,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.
@@ -8012,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",
@@ -8021,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)))
@@ -8058,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);
@@ -8084,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;
@@ -8146,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);
                }
@@ -8202,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;
@@ -8240,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 */
@@ -8264,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) {
@@ -8310,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;
 }
 
@@ -8390,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) {
@@ -8421,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,
@@ -8445,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)) {
@@ -8495,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,
@@ -8553,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;
 
@@ -8566,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;
 
@@ -8607,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;
        }
@@ -8619,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 --
@@ -8628,22 +7987,21 @@ 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_include *tmpi;
-       struct ast_sw *sw;
        struct ast_exten *e, *el, *en;
-       struct ast_ignorepat *ipi;
        struct ast_context *tmp = con;
 
-       for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
-               struct ast_include *tmpil = tmpi;
-               tmpi = tmpi->next;
-               ast_free(tmpil);
-       }
-       for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
-               struct ast_ignorepat *ipl = ipi;
-               ipi = ipi->next;
-               ast_free(ipl);
-       }
+       /* Free includes */
+       AST_VECTOR_CALLBACK_VOID(&tmp->includes, include_free);
+       AST_VECTOR_FREE(&tmp->includes);
+
+       /* Free ignorepats */
+       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);
 
@@ -8655,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;
@@ -8703,53 +8059,35 @@ 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_ignorepat *ip, *ipl = NULL, *ipn = NULL;
-                       struct ast_include *i, *pi = NULL, *ni = NULL;
-                       struct ast_sw *sw = NULL;
+                       int idx;
 
                        /* remove any ignorepats whose registrar matches */
-                       for (ip = tmp->ignorepats; ip; ip = ipn) {
-                               ipn = ip->next;
-                               if (!strcmp(ip->registrar, registrar)) {
-                                       if (ipl) {
-                                               ipl->next = ip->next;
-                                               ast_free(ip);
-                                               continue; /* don't change ipl */
-                                       } else {
-                                               tmp->ignorepats = ip->next;
-                                               ast_free(ip);
-                                               continue; /* don't change ipl */
-                                       }
+                       for (idx = ast_context_ignorepats_count(tmp) - 1; idx >= 0; idx--) {
+                               struct ast_ignorepat *ip = AST_VECTOR_GET(&tmp->ignorepats, idx);
+
+                               if (!strcmp(ast_get_ignorepat_registrar(ip), registrar)) {
+                                       AST_VECTOR_REMOVE_ORDERED(&tmp->ignorepats, idx);
+                                       ignorepat_free(ip);
                                }
-                               ipl = ip;
                        }
                        /* remove any includes whose registrar matches */
-                       for (i = tmp->includes; i; i = ni) {
-                               ni = i->next;
-                               if (strcmp(i->registrar, registrar) == 0) {
-                                       /* remove from list */
-                                       if (pi) {
-                                               pi->next = i->next;
-                                               /* free include */
-                                               ast_free(i);
-                                               continue; /* don't change pi */
-                                       } else {
-                                               tmp->includes = i->next;
-                                               /* free include */
-                                               ast_free(i);
-                                               continue; /* don't change pi */
-                                       }
+                       for (idx = ast_context_includes_count(tmp) - 1; idx >= 0; idx--) {
+                               struct ast_include *i = AST_VECTOR_GET(&tmp->includes, idx);
+
+                               if (!strcmp(ast_get_include_registrar(i), registrar)) {
+                                       AST_VECTOR_REMOVE_ORDERED(&tmp->includes, idx);
+                                       include_free(i);
                                }
-                               pi = i;
                        }
                        /* 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);
@@ -8773,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));
@@ -8800,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 && !tmp->ignorepats && !tmp->includes && 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);
 
@@ -8933,14 +8271,10 @@ int pbx_checkcondition(const char *condition)
 static void presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
        struct ast_presence_state_message *presence_state;
-       struct ast_hint *hint;
        struct ast_str *hint_app = NULL;
        struct ast_hintdevice *device;
        struct ast_hintdevice *cmpdevice;
        struct ao2_iterator *dev_iter;
-       struct ao2_iterator cb_iter;
-       char context_name[AST_MAX_CONTEXT];
-       char exten_name[AST_MAX_EXTENSION];
 
        if (stasis_message_type(msg) != ast_presence_state_message_type()) {
                return;
@@ -8974,74 +8308,9 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
        }
 
        for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
-               struct ast_state_cb *state_cb;
-
-               if (!device->hint) {
-                       /* Should never happen. */
-                       continue;
-               }
-               hint = device->hint;
-
-               ao2_lock(hint);
-               if (!hint->exten) {
-                       /* The extension has already been destroyed */
-                       ao2_unlock(hint);
-                       continue;
-               }
-
-               /*
-                * Save off strings in case the hint extension gets destroyed
-                * while we are notifying the watchers.
-                */
-               ast_copy_string(context_name,
-                       ast_get_context_name(ast_get_extension_context(hint->exten)),
-                       sizeof(context_name));
-               ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
-                       sizeof(exten_name));
-               ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-               ao2_unlock(hint);
-
-               /* Check to see if update is necessary */
-               if ((hint->last_presence_state == presence_state->state) &&
-                       ((hint->last_presence_subtype && presence_state->subtype && !strcmp(hint->last_presence_subtype, presence_state->subtype)) || (!hint->last_presence_subtype && !presence_state->subtype)) &&
-                       ((hint->last_presence_message && presence_state->message && !strcmp(hint->last_presence_message, presence_state->message)) || (!hint->last_presence_message && !presence_state->message))) {
-
-                       /* this update is the same as the last, do nothing */
-                       continue;
-               }
-
-               /* update new values */
-               ast_free(hint->last_presence_subtype);
-               ast_free(hint->last_presence_message);
-               hint->last_presence_state = presence_state->state;
-               hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
-               hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
-
-               /* For general callbacks */
-               cb_iter = ao2_iterator_init(statecbs, 0);
-               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-                       execute_state_callback(state_cb->change_cb,
-                               context_name,
-                               exten_name,
-                               state_cb->data,
-                               AST_HINT_UPDATE_PRESENCE,
-                               hint,
-                               NULL);
-               }
-               ao2_iterator_destroy(&cb_iter);
-
-               /* For extension callbacks */
-               cb_iter = ao2_iterator_init(hint->callbacks, 0);
-               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
-                       execute_state_callback(state_cb->change_cb,
-                               context_name,
-                               exten_name,
-                               state_cb->data,
-                               AST_HINT_UPDATE_PRESENCE,
-                               hint,
-                               NULL);
+               if (device->hint) {
+                       presence_state_notify_callbacks(device->hint, &hint_app, presence_state);
                }
-               ao2_iterator_destroy(&cb_iter);
        }
        ao2_iterator_destroy(dev_iter);
        ast_mutex_unlock(&context_merge_lock);
@@ -9049,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");
@@ -9174,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)
@@ -9188,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);
 
@@ -9262,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)
@@ -9270,16 +8487,6 @@ const char *ast_get_extension_label(struct ast_exten *exten)
        return exten ? exten->label : NULL;
 }
 
-const char *ast_get_include_name(struct ast_include *inc)
-{
-       return inc ? inc->name : NULL;
-}
-
-const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
-{
-       return ip ? ip->pattern : NULL;
-}
-
 int ast_get_extension_priority(struct ast_exten *exten)
 {
        return exten ? exten->priority : -1;
@@ -9298,14 +8505,14 @@ const char *ast_get_extension_registrar(struct ast_exten *e)
        return e ? e->registrar : NULL;
 }
 
-const char *ast_get_include_registrar(struct ast_include *i)
+const char *ast_get_extension_registrar_file(struct ast_exten *e)
 {
-       return i ? i->registrar : NULL;
+       return e ? e->registrar_file : NULL;
 }
 
-const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
+int ast_get_extension_registrar_line(struct ast_exten *e)
 {
-       return ip ? ip->registrar : NULL;
+       return e ? e->registrar_line : 0;
 }
 
 int ast_get_extension_matchcid(struct ast_exten *e)
@@ -9315,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)
@@ -9328,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 ...
  */
@@ -9365,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,
@@ -9380,36 +8597,103 @@ struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
        return priority ? priority->peer : exten;
 }
 
-struct ast_include *ast_walk_context_includes(struct ast_context *con,
-       struct ast_include *inc)
+const struct ast_include *ast_walk_context_includes(const struct ast_context *con,
+       const struct ast_include *inc)
 {
-       if (!inc)
-               return con ? con->includes : NULL;
-       else
-               return inc->next;
+       if (inc) {
+               int idx;
+               int next = 0;
+
+               for (idx = 0; idx < ast_context_includes_count(con); idx++) {
+                       const struct ast_include *include = AST_VECTOR_GET(&con->includes, idx);
+
+                       if (next) {
+                               return include;
+                       }
+
+                       if (inc == include) {
+                               next = 1;
+                       }
+               }
+
+               return NULL;
+       }
+
+       if (!ast_context_includes_count(con)) {
+               return NULL;
+       }
+
+       return ast_context_includes_get(con, 0);
 }
 
-struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
-       struct ast_ignorepat *ip)
+int ast_context_includes_count(const struct ast_context *con)
 {
-       if (!ip)
-               return con ? con->ignorepats : NULL;
-       else
-               return ip->next;
+       return AST_VECTOR_SIZE(&con->includes);
+}
+
+const struct ast_include *ast_context_includes_get(const struct ast_context *con, int idx)
+{
+       return AST_VECTOR_GET(&con->includes, idx);
+}
+
+const struct ast_ignorepat *ast_walk_context_ignorepats(const struct ast_context *con,
+       const struct ast_ignorepat *ip)
+{
+       if (!con) {
+               return NULL;
+       }
+
+       if (ip) {
+               int idx;
+               int next = 0;
+
+               for (idx = 0; idx < ast_context_ignorepats_count(con); idx++) {
+                       const struct ast_ignorepat *i = ast_context_ignorepats_get(con, idx);
+
+                       if (next) {
+                               return i;
+                       }
+
+                       if (ip == i) {
+                               next = 1;
+                       }
+               }
+
+               return NULL;
+       }
+
+       if (!ast_context_ignorepats_count(con)) {
+               return NULL;
+       }
+
+       return ast_context_ignorepats_get(con, 0);
+}
+
+int ast_context_ignorepats_count(const struct ast_context *con)
+{
+       return AST_VECTOR_SIZE(&con->ignorepats);
+}
+
+const struct ast_ignorepat *ast_context_ignorepats_get(const struct ast_context *con, int idx)
+{
+       return AST_VECTOR_GET(&con->ignorepats, idx);
 }
 
 int ast_context_verify_includes(struct ast_context *con)
 {
-       struct ast_include *inc = NULL;
+       int idx;
        int res = 0;
 
-       while ( (inc = ast_walk_context_includes(con, inc)) ) {
-               if (ast_context_find(inc->rname))
+       for (idx = 0; idx < ast_context_includes_count(con); idx++) {
+               const struct ast_include *inc = ast_context_includes_get(con, idx);
+
+               if (ast_context_find(include_rname(inc))) {
                        continue;
+               }
 
                res = -1;
                ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
-                       ast_get_context_name(con), inc->rname);
+                       ast_get_context_name(con), include_rname(inc));
                break;
        }
 
@@ -9513,37 +8797,6 @@ int ast_async_parseable_goto(struct ast_channel *chan, const char *goto_string)
        return pbx_parseable_goto(chan, goto_string, 1);
 }
 
-char *ast_complete_applications(const char *line, const char *word, int state)
-{
-       struct ast_app *app;
-       int which = 0;
-       int cmp;
-       char *ret = NULL;
-       size_t wordlen = strlen(word);
-
-       AST_RWLIST_RDLOCK(&apps);
-       AST_RWLIST_TRAVERSE(&apps, app, list) {
-               cmp = strncasecmp(word, app->name, wordlen);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found match. */
-                       if (++which <= state) {
-                               /* Not enough matches. */
-                               continue;
-                       }
-                       ret = ast_strdup(app->name);
-                       break;
-               }
-               /* Not in container. */
-               break;
-       }
-       AST_RWLIST_UNLOCK(&apps);
-
-       return ret;
-}
-
 static int hint_hash(const void *obj, const int flags)
 {
        const struct ast_hint *hint = obj;
@@ -9586,6 +8839,8 @@ static int statecbs_cmp(void *obj, void *arg, int flags)
  */
 static void pbx_shutdown(void)
 {
+       STASIS_MESSAGE_TYPE_CLEANUP(hint_change_message_type);
+
        if (hints) {
                ao2_container_unregister("hints");
                ao2_ref(hints, -1);
@@ -9596,6 +8851,11 @@ static void pbx_shutdown(void)
                ao2_ref(hintdevices, -1);
                hintdevices = NULL;
        }
+       if (autohints) {
+               ao2_container_unregister("autohints");
+               ao2_ref(autohints, -1);
+               autohints = NULL;
+       }
        if (statecbs) {
                ao2_container_unregister("statecbs");
                ao2_ref(statecbs, -1);
@@ -9629,6 +8889,16 @@ static void print_hintdevices_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
                ast_get_context_name(ast_get_extension_context(hintdevice->hint->exten)));
 }
 
+static void print_autohint_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+       struct ast_autohint *autohint = v_obj;
+
+       if (!autohint) {
+               return;
+       }
+       prnt(where, "%s", autohint->context);
+}
+
 static void print_statecbs_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
 {
        struct ast_state_cb *state_cb = v_obj;
@@ -9649,6 +8919,12 @@ int ast_pbx_init(void)
        if (hintdevices) {
                ao2_container_register("hintdevices", hintdevices, print_hintdevices_key);
        }
+       /* This is protected by the context_and_merge lock */
+       autohints = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, HASH_EXTENHINT_SIZE,
+               autohint_hash_cb, autohint_cmp);
+       if (hintdevices) {
+               ao2_container_register("autohints", autohints, print_autohint_key);
+       }
        statecbs = ao2_container_alloc(1, NULL, statecbs_cmp);
        if (statecbs) {
                ao2_container_register("statecbs", statecbs, print_statecbs_key);
@@ -9656,5 +8932,9 @@ int ast_pbx_init(void)
 
        ast_register_cleanup(pbx_shutdown);
 
-       return (hints && hintdevices && statecbs) ? 0 : -1;
+       if (STASIS_MESSAGE_TYPE_INIT(hint_change_message_type) != 0) {
+               return -1;
+       }
+
+       return (hints && hintdevices && autohints && statecbs) ? 0 : -1;
 }