loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / pbx.c
index 99c6863..b5602b5 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/_private.h"
 #include "asterisk/paths.h"    /* use ast_config_AST_SYSTEM_NAME */
 #include <ctype.h>
@@ -72,6 +70,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/astobj2.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/dial.h"
+#include "asterisk/vector.h"
+#include "pbx_private.h"
 
 /*!
  * \note I M P O R T A N T :
@@ -95,641 +95,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  */
 
 /*** DOCUMENTATION
-       <application name="Answer" language="en_US">
-               <synopsis>
-                       Answer a channel if ringing.
-               </synopsis>
-               <syntax>
-                       <parameter name="delay">
-                               <para>Asterisk will wait this number of milliseconds before returning to
-                               the dialplan after answering the call.</para>
-                       </parameter>
-                       <parameter name="nocdr">
-                               <para>Asterisk will send an answer signal to the calling phone, but will not
-                               set the disposition or answer time in the CDR for this call.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>If the call has not been answered, this application will
-                       answer it. Otherwise, it has no effect on the call.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Hangup</ref>
-               </see-also>
-       </application>
-       <application name="BackGround" language="en_US">
-               <synopsis>
-                       Play an audio file while waiting for digits of an extension to go to.
-               </synopsis>
-               <syntax>
-                       <parameter name="filenames" required="true" argsep="&amp;">
-                               <argument name="filename1" required="true" />
-                               <argument name="filename2" multiple="true" />
-                       </parameter>
-                       <parameter name="options">
-                               <optionlist>
-                                       <option name="s">
-                                               <para>Causes the playback of the message to be skipped
-                                               if the channel is not in the <literal>up</literal> state (i.e. it
-                                               hasn't been answered yet). If this happens, the
-                                               application will return immediately.</para>
-                                       </option>
-                                       <option name="n">
-                                               <para>Don't answer the channel before playing the files.</para>
-                                       </option>
-                                       <option name="m">
-                                               <para>Only break if a digit hit matches a one digit
-                                               extension in the destination context.</para>
-                                       </option>
-                               </optionlist>
-                       </parameter>
-                       <parameter name="langoverride">
-                               <para>Explicitly specifies which language to attempt to use for the requested sound files.</para>
-                       </parameter>
-                       <parameter name="context">
-                               <para>This is the dialplan context that this application will use when exiting
-                               to a dialed extension.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will play the given list of files <emphasis>(do not put extension)</emphasis>
-                       while waiting for an extension to be dialed by the calling channel. To continue waiting
-                       for digits after this application has finished playing files, the <literal>WaitExten</literal>
-                       application should be used.</para>
-                       <para>If one of the requested sound files does not exist, call processing will be terminated.</para>
-                       <para>This application sets the following channel variable upon completion:</para>
-                       <variablelist>
-                               <variable name="BACKGROUNDSTATUS">
-                                       <para>The status of the background attempt as a text string.</para>
-                                       <value name="SUCCESS" />
-                                       <value name="FAILED" />
-                               </variable>
-                       </variablelist>
-               </description>
-               <see-also>
-                       <ref type="application">ControlPlayback</ref>
-                       <ref type="application">WaitExten</ref>
-                       <ref type="application">BackgroundDetect</ref>
-                       <ref type="function">TIMEOUT</ref>
-               </see-also>
-       </application>
-       <application name="Busy" language="en_US">
-               <synopsis>
-                       Indicate the Busy condition.
-               </synopsis>
-               <syntax>
-                       <parameter name="timeout">
-                               <para>If specified, the calling channel will be hung up after the specified number of seconds.
-                               Otherwise, this application will wait until the calling channel hangs up.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will indicate the busy condition to the calling channel.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Congestion</ref>
-                       <ref type="application">Progress</ref>
-                       <ref type="application">Playtones</ref>
-                       <ref type="application">Hangup</ref>
-               </see-also>
-       </application>
-       <application name="Congestion" language="en_US">
-               <synopsis>
-                       Indicate the Congestion condition.
-               </synopsis>
-               <syntax>
-                       <parameter name="timeout">
-                               <para>If specified, the calling channel will be hung up after the specified number of seconds.
-                               Otherwise, this application will wait until the calling channel hangs up.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will indicate the congestion condition to the calling channel.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Busy</ref>
-                       <ref type="application">Progress</ref>
-                       <ref type="application">Playtones</ref>
-                       <ref type="application">Hangup</ref>
-               </see-also>
-       </application>
-       <application name="ExecIfTime" language="en_US">
-               <synopsis>
-                       Conditional application execution based on the current time.
-               </synopsis>
-               <syntax argsep="?">
-                       <parameter name="day_condition" required="true">
-                               <argument name="times" required="true" />
-                               <argument name="weekdays" required="true" />
-                               <argument name="mdays" required="true" />
-                               <argument name="months" required="true" />
-                               <argument name="timezone" required="false" />
-                       </parameter>
-                       <parameter name="appname" required="true" hasparams="optional">
-                               <argument name="appargs" required="true" />
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will execute the specified dialplan application, with optional
-                       arguments, if the current time matches the given time specification.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Exec</ref>
-                       <ref type="application">ExecIf</ref>
-                       <ref type="application">TryExec</ref>
-                       <ref type="application">GotoIfTime</ref>
-               </see-also>
-       </application>
-       <application name="Goto" language="en_US">
-               <synopsis>
-                       Jump to a particular priority, extension, or context.
-               </synopsis>
-               <syntax>
-                       <parameter name="context" />
-                       <parameter name="extensions" />
-                       <parameter name="priority" required="true" />
-               </syntax>
-               <description>
-                       <para>This application will set the current context, extension, and priority in the channel structure.
-                       After it completes, the pbx engine will continue dialplan execution at the specified location.
-                       If no specific <replaceable>extension</replaceable>, or <replaceable>extension</replaceable> and
-                       <replaceable>context</replaceable>, are specified, then this application will
-                       just set the specified <replaceable>priority</replaceable> of the current extension.</para>
-                       <para>At least a <replaceable>priority</replaceable> is required as an argument, or the goto will
-                       return a <literal>-1</literal>, and the channel and call will be terminated.</para>
-                       <para>If the location that is put into the channel information is bogus, and asterisk cannot
-                       find that location in the dialplan, then the execution engine will try to find and execute the code in
-                       the <literal>i</literal> (invalid) extension in the current context. If that does not exist, it will try to execute the
-                       <literal>h</literal> extension. If neither the <literal>h</literal> nor <literal>i</literal> extensions
-                       have been defined, the channel is hung up, and the execution of instructions on the channel is terminated.
-                       What this means is that, for example, you specify a context that does not exist, then
-                       it will not be possible to find the <literal>h</literal> or <literal>i</literal> extensions,
-                       and the call will terminate!</para>
-               </description>
-               <see-also>
-                       <ref type="application">GotoIf</ref>
-                       <ref type="application">GotoIfTime</ref>
-                       <ref type="application">Gosub</ref>
-                       <ref type="application">Macro</ref>
-               </see-also>
-       </application>
-       <application name="GotoIf" language="en_US">
-               <synopsis>
-                       Conditional goto.
-               </synopsis>
-               <syntax argsep="?">
-                       <parameter name="condition" required="true" />
-                       <parameter name="destination" required="true" argsep=":">
-                               <argument name="labeliftrue">
-                                       <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
-                                       Takes the form similar to Goto() of [[context,]extension,]priority.</para>
-                               </argument>
-                               <argument name="labeliffalse">
-                                       <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
-                                       Takes the form similar to Goto() of [[context,]extension,]priority.</para>
-                               </argument>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will set the current context, extension, and priority in the channel structure
-                       based on the evaluation of the given condition. After this application completes, the
-                       pbx engine will continue dialplan execution at the specified location in the dialplan.
-                       The labels are specified with the same syntax as used within the Goto application.
-                       If the label chosen by the condition is omitted, no jump is performed, and the execution passes to the
-                       next instruction. If the target location is bogus, and does not exist, the execution engine will try
-                       to find and execute the code in the <literal>i</literal> (invalid) extension in the current context.
-                       If that does not exist, it will try to execute the <literal>h</literal> extension.
-                       If neither the <literal>h</literal> nor <literal>i</literal> extensions have been defined,
-                       the channel is hung up, and the execution of instructions on the channel is terminated.
-                       Remember that this command can set the current context, and if the context specified
-                       does not exist, then it will not be able to find any 'h' or 'i' extensions there, and
-                       the channel and call will both be terminated!.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Goto</ref>
-                       <ref type="application">GotoIfTime</ref>
-                       <ref type="application">GosubIf</ref>
-                       <ref type="application">MacroIf</ref>
-               </see-also>
-       </application>
-       <application name="GotoIfTime" language="en_US">
-               <synopsis>
-                       Conditional Goto based on the current time.
-               </synopsis>
-               <syntax argsep="?">
-                       <parameter name="condition" required="true">
-                               <argument name="times" required="true" />
-                               <argument name="weekdays" required="true" />
-                               <argument name="mdays" required="true" />
-                               <argument name="months" required="true" />
-                               <argument name="timezone" required="false" />
-                       </parameter>
-                       <parameter name="destination" required="true" argsep=":">
-                               <argument name="labeliftrue">
-                                       <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
-                                       Takes the form similar to Goto() of [[context,]extension,]priority.</para>
-                               </argument>
-                               <argument name="labeliffalse">
-                                       <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
-                                       Takes the form similar to Goto() of [[context,]extension,]priority.</para>
-                               </argument>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will set the context, extension, and priority in the channel structure
-                       based on the evaluation of the given time specification. After this application completes,
-                       the pbx engine will continue dialplan execution at the specified location in the dialplan.
-                       If the current time is within the given time specification, the channel will continue at
-                       <replaceable>labeliftrue</replaceable>. Otherwise the channel will continue at <replaceable>labeliffalse</replaceable>.
-                       If the label chosen by the condition is omitted, no jump is performed, and execution passes to the next
-                       instruction. If the target jump location is bogus, the same actions would be taken as for <literal>Goto</literal>.
-                       Further information on the time specification can be found in examples
-                       illustrating how to do time-based context includes in the dialplan.</para>
-               </description>
-               <see-also>
-                       <ref type="application">GotoIf</ref>
-                       <ref type="application">Goto</ref>
-                       <ref type="function">IFTIME</ref>
-                       <ref type="function">TESTTIME</ref>
-               </see-also>
-       </application>
-       <application name="ImportVar" language="en_US">
-               <synopsis>
-                       Import a variable from a channel into a new variable.
-               </synopsis>
-               <syntax argsep="=">
-                       <parameter name="newvar" required="true" />
-                       <parameter name="vardata" required="true">
-                               <argument name="channelname" required="true" />
-                               <argument name="variable" required="true" />
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application imports a <replaceable>variable</replaceable> from the specified
-                       <replaceable>channel</replaceable> (as opposed to the current one) and stores it as a variable
-                       (<replaceable>newvar</replaceable>) in the current channel (the channel that is calling this
-                       application). Variables created by this application have the same inheritance properties as those
-                       created with the <literal>Set</literal> application.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Set</ref>
-               </see-also>
-       </application>
-       <application name="Hangup" language="en_US">
-               <synopsis>
-                       Hang up the calling channel.
-               </synopsis>
-               <syntax>
-                       <parameter name="causecode">
-                               <para>If a <replaceable>causecode</replaceable> is given the channel's
-                               hangup cause will be set to the given value.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application will hang up the calling channel.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Answer</ref>
-                       <ref type="application">Busy</ref>
-                       <ref type="application">Congestion</ref>
-               </see-also>
-       </application>
-       <application name="Incomplete" language="en_US">
-               <synopsis>
-                       Returns AST_PBX_INCOMPLETE value.
-               </synopsis>
-               <syntax>
-                       <parameter name="n">
-                               <para>If specified, then Incomplete will not attempt to answer the channel first.</para>
-                               <note><para>Most channel types need to be in Answer state in order to receive DTMF.</para></note>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>Signals the PBX routines that the previous matched extension is incomplete
-                       and that further input should be allowed before matching can be considered
-                       to be complete.  Can be used within a pattern match when certain criteria warrants
-                       a longer match.</para>
-               </description>
-       </application>
-       <application name="NoOp" language="en_US">
-               <synopsis>
-                       Do Nothing (No Operation).
-               </synopsis>
-               <syntax>
-                       <parameter name="text">
-                               <para>Any text provided can be viewed at the Asterisk CLI.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application does nothing. However, it is useful for debugging purposes.</para>
-                       <para>This method can be used to see the evaluations of variables or functions without having any effect.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Verbose</ref>
-                       <ref type="application">Log</ref>
-               </see-also>
-       </application>
-       <application name="Proceeding" language="en_US">
-               <synopsis>
-                       Indicate proceeding.
-               </synopsis>
-               <syntax />
-               <description>
-                       <para>This application will request that a proceeding message be provided to the calling channel.</para>
-               </description>
-       </application>
-       <application name="Progress" language="en_US">
-               <synopsis>
-                       Indicate progress.
-               </synopsis>
-               <syntax />
-               <description>
-                       <para>This application will request that in-band progress information be provided to the calling channel.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Busy</ref>
-                       <ref type="application">Congestion</ref>
-                       <ref type="application">Ringing</ref>
-                       <ref type="application">Playtones</ref>
-               </see-also>
-       </application>
-       <application name="RaiseException" language="en_US">
-               <synopsis>
-                       Handle an exceptional condition.
-               </synopsis>
-               <syntax>
-                       <parameter name="reason" required="true" />
-               </syntax>
-               <description>
-                       <para>This application will jump to the <literal>e</literal> extension in the current context, setting the
-                       dialplan function EXCEPTION(). If the <literal>e</literal> extension does not exist, the call will hangup.</para>
-               </description>
-               <see-also>
-                       <ref type="function">Exception</ref>
-               </see-also>
-       </application>
-       <application name="Ringing" language="en_US">
-               <synopsis>
-                       Indicate ringing tone.
-               </synopsis>
-               <syntax />
-               <description>
-                       <para>This application will request that the channel indicate a ringing tone to the user.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Busy</ref>
-                       <ref type="application">Congestion</ref>
-                       <ref type="application">Progress</ref>
-                       <ref type="application">Playtones</ref>
-               </see-also>
-       </application>
-       <application name="SayAlpha" language="en_US">
-               <synopsis>
-                       Say Alpha.
-               </synopsis>
-               <syntax>
-                       <parameter name="string" required="true" />
-               </syntax>
-               <description>
-                       <para>This application will play the sounds that correspond to the letters
-                       of the given <replaceable>string</replaceable>. If the channel variable
-                       <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive),
-                       then this application will react to DTMF in the same way as
-                       <literal>Background</literal>.</para>
-               </description>
-               <see-also>
-                       <ref type="application">SayDigits</ref>
-                       <ref type="application">SayNumber</ref>
-                       <ref type="application">SayPhonetic</ref>
-                       <ref type="function">CHANNEL</ref>
-               </see-also>
-       </application>
-       <application name="SayAlphaCase" language="en_US">
-               <synopsis>
-                       Say Alpha.
-               </synopsis>
-               <syntax>
-                       <parameter name="casetype" required="true" >
-                               <enumlist>
-                                       <enum name="a">
-                                               <para>Case sensitive (all) pronunciation.
-                                               (Ex: SayAlphaCase(a,aBc); - lowercase a uppercase b lowercase c).</para>
-                                       </enum>
-                                       <enum name="l">
-                                               <para>Case sensitive (lower) pronunciation.
-                                               (Ex: SayAlphaCase(l,aBc); - lowercase a b lowercase c).</para>
-                                       </enum>
-                                       <enum name="n">
-                                               <para>Case insensitive pronunciation. Equivalent to SayAlpha.
-                                               (Ex: SayAlphaCase(n,aBc) - a b c).</para>
-                                       </enum>
-                                       <enum name="u">
-                                               <para>Case sensitive (upper) pronunciation.
-                                               (Ex: SayAlphaCase(u,aBc); - a uppercase b c).</para>
-                                       </enum>
-                               </enumlist>
-                       </parameter>
-                       <parameter name="string" required="true" />
-               </syntax>
-               <description>
-                       <para>This application will play the sounds that correspond to the letters of the
-                       given <replaceable>string</replaceable>.  Optionally, a <replaceable>casetype</replaceable> may be
-                       specified.  This will be used for case-insensitive or case-sensitive pronunciations. If the channel
-                       variable <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
-                       application will react to DTMF in the same way as <literal>Background</literal>.</para>
-               </description>
-               <see-also>
-                       <ref type="application">SayDigits</ref>
-                       <ref type="application">SayNumber</ref>
-                       <ref type="application">SayPhonetic</ref>
-                       <ref type="application">SayAlpha</ref>
-                       <ref type="function">CHANNEL</ref>
-               </see-also>
-       </application>
-       <application name="SayDigits" language="en_US">
-               <synopsis>
-                       Say Digits.
-               </synopsis>
-               <syntax>
-                       <parameter name="digits" required="true" />
-               </syntax>
-               <description>
-                       <para>This application will play the sounds that correspond to the digits of
-                       the given number. This will use the language that is currently set for the channel.
-                       If the channel variable <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true'
-                       (case insensitive), then this application will react to DTMF in the same way as
-                       <literal>Background</literal>.</para>
-               </description>
-               <see-also>
-                       <ref type="application">SayAlpha</ref>
-                       <ref type="application">SayNumber</ref>
-                       <ref type="application">SayPhonetic</ref>
-                       <ref type="function">CHANNEL</ref>
-               </see-also>
-       </application>
-       <application name="SayNumber" language="en_US">
-               <synopsis>
-                       Say Number.
-               </synopsis>
-               <syntax>
-                       <parameter name="digits" required="true" />
-                       <parameter name="gender" />
-               </syntax>
-               <description>
-                       <para>This application will play the sounds that correspond to the given
-                       <replaceable>digits</replaceable>. Optionally, a <replaceable>gender</replaceable> may be
-                       specified. This will use the language that is currently set for the channel. See the CHANNEL()
-                       function for more information on setting the language for the channel. If the channel variable
-                       <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
-                       application will react to DTMF in the same way as <literal>Background</literal>.</para>
-               </description>
-               <see-also>
-                       <ref type="application">SayAlpha</ref>
-                       <ref type="application">SayDigits</ref>
-                       <ref type="application">SayPhonetic</ref>
-                       <ref type="function">CHANNEL</ref>
-               </see-also>
-       </application>
-       <application name="SayPhonetic" language="en_US">
-               <synopsis>
-                       Say Phonetic.
-               </synopsis>
-               <syntax>
-                       <parameter name="string" required="true" />
-               </syntax>
-               <description>
-                       <para>This application will play the sounds from the phonetic alphabet that correspond to the
-                       letters in the given <replaceable>string</replaceable>. If the channel variable
-                       <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
-                       application will react to DTMF in the same way as <literal>Background</literal>.</para>
-               </description>
-               <see-also>
-                       <ref type="application">SayAlpha</ref>
-                       <ref type="application">SayDigits</ref>
-                       <ref type="application">SayNumber</ref>
-               </see-also>
-       </application>
-       <application name="Set" language="en_US">
-               <synopsis>
-                       Set channel variable or function value.
-               </synopsis>
-               <syntax argsep="=">
-                       <parameter name="name" required="true" />
-                       <parameter name="value" required="true" />
-               </syntax>
-               <description>
-                       <para>This function can be used to set the value of channel variables or dialplan functions.
-                       When setting variables, if the variable name is prefixed with <literal>_</literal>,
-                       the variable will be inherited into channels created from the current channel.
-                       If the variable name is prefixed with <literal>__</literal>, the variable will be
-                       inherited into channels created from the current channel and all children channels.</para>
-                       <note><para>If (and only if), in <filename>/etc/asterisk/asterisk.conf</filename>, you have
-                       a <literal>[compat]</literal> category, and you have <literal>app_set = 1.4</literal> under that, then
-                       the behavior of this app changes, and strips surrounding quotes from the right hand side as
-                       it did previously in 1.4.
-                       The advantages of not stripping out quoting, and not caring about the separator characters (comma and vertical bar)
-                       were sufficient to make these changes in 1.6. Confusion about how many backslashes would be needed to properly
-                       protect separators and quotes in various database access strings has been greatly
-                       reduced by these changes.</para></note>
-               </description>
-               <see-also>
-                       <ref type="application">MSet</ref>
-                       <ref type="function">GLOBAL</ref>
-                       <ref type="function">SET</ref>
-                       <ref type="function">ENV</ref>
-               </see-also>
-       </application>
-       <application name="MSet" language="en_US">
-               <synopsis>
-                       Set channel variable(s) or function value(s).
-               </synopsis>
-               <syntax>
-                       <parameter name="set1" required="true" argsep="=">
-                               <argument name="name1" required="true" />
-                               <argument name="value1" required="true" />
-                       </parameter>
-                       <parameter name="set2" multiple="true" argsep="=">
-                               <argument name="name2" required="true" />
-                               <argument name="value2" required="true" />
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This function can be used to set the value of channel variables or dialplan functions.
-                       When setting variables, if the variable name is prefixed with <literal>_</literal>,
-                       the variable will be inherited into channels created from the current channel
-                       If the variable name is prefixed with <literal>__</literal>, the variable will be
-                       inherited into channels created from the current channel and all children channels.
-                       MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus
-                       prone to doing things that you may not expect. For example, it strips surrounding
-                       double-quotes from the right-hand side (value). If you need to put a separator
-                       character (comma or vert-bar), you will need to escape them by inserting a backslash
-                       before them. Avoid its use if possible.</para>
-               </description>
-               <see-also>
-                       <ref type="application">Set</ref>
-               </see-also>
-       </application>
-       <application name="SetAMAFlags" language="en_US">
-               <synopsis>
-                       Set the AMA Flags.
-               </synopsis>
-               <syntax>
-                       <parameter name="flag" />
-               </syntax>
-               <description>
-                       <para>This application will set the channel's AMA Flags for billing purposes.</para>
-                       <warning><para>This application is deprecated. Please use the CHANNEL function instead.</para></warning>
-               </description>
-               <see-also>
-                       <ref type="function">CDR</ref>
-                       <ref type="function">CHANNEL</ref>
-               </see-also>
-       </application>
-       <application name="Wait" language="en_US">
-               <synopsis>
-                       Waits for some time.
-               </synopsis>
-               <syntax>
-                       <parameter name="seconds" required="true">
-                               <para>Can be passed with fractions of a second. For example, <literal>1.5</literal> will ask the
-                               application to wait for 1.5 seconds.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application waits for a specified number of <replaceable>seconds</replaceable>.</para>
-               </description>
-       </application>
-       <application name="WaitExten" language="en_US">
-               <synopsis>
-                       Waits for an extension to be entered.
-               </synopsis>
-               <syntax>
-                       <parameter name="seconds">
-                               <para>Can be passed with fractions of a second. For example, <literal>1.5</literal> will ask the
-                               application to wait for 1.5 seconds.</para>
-                       </parameter>
-                       <parameter name="options">
-                               <optionlist>
-                                       <option name="m">
-                                               <para>Provide music on hold to the caller while waiting for an extension.</para>
-                                               <argument name="x">
-                                                       <para>Specify the class for music on hold. <emphasis>CHANNEL(musicclass) will
-                                                       be used instead if set</emphasis></para>
-                                               </argument>
-                                       </option>
-                               </optionlist>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>This application waits for the user to enter a new extension for a specified number
-                       of <replaceable>seconds</replaceable>.</para>
-                       <xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
-               </description>
-               <see-also>
-                       <ref type="application">Background</ref>
-                       <ref type="function">TIMEOUT</ref>
-               </see-also>
-       </application>
        <function name="EXCEPTION" language="en_US">
                <synopsis>
                        Retrieve the details of the current dialplan exception.
@@ -804,6 +169,45 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        may take a lot of capacity.</para>
                </description>
        </manager>
+       <manager name="ExtensionStateList" language="en_US">
+               <synopsis>
+                       List the current known extension states.
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>This will list out all known extension states in a
+                       sequence of <replaceable>ExtensionStatus</replaceable> events.
+                       When finished, a <replaceable>ExtensionStateListComplete</replaceable> event
+                       will be emitted.</para>
+               </description>
+               <see-also>
+                       <ref type="manager">ExtensionState</ref>
+                       <ref type="function">HINT</ref>
+                       <ref type="function">EXTENSION_STATE</ref>
+               </see-also>
+               <responses>
+                       <list-elements>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='ExtensionStatus'])" />
+                       </list-elements>
+                       <managerEvent name="ExtensionStateListComplete" language="en_US">
+                               <managerEventInstance class="EVENT_FLAG_COMMAND">
+                                       <synopsis>
+                                               Indicates the end of the list the current known extension states.
+                                       </synopsis>
+                                       <syntax>
+                                               <parameter name="EventList">
+                                                       <para>Conveys the status of the event list.</para>
+                                               </parameter>
+                                               <parameter name="ListItems">
+                                                       <para>Conveys the number of statuses reported.</para>
+                                               </parameter>
+                                       </syntax>
+                               </managerEventInstance>
+                       </managerEvent>
+               </responses>
+       </manager>
  ***/
 
 #ifdef LOW_MEMORY
@@ -814,32 +218,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define SWITCH_DATA_LENGTH 256
 
-#define VAR_BUF_SIZE 4096
-
 #define        VAR_NORMAL              1
 #define        VAR_SOFTTRAN    2
 #define        VAR_HARDTRAN    3
 
-#define BACKGROUND_SKIP                (1 << 0)
-#define BACKGROUND_NOANSWER    (1 << 1)
-#define BACKGROUND_MATCHEXTEN  (1 << 2)
-#define BACKGROUND_PLAYBACK    (1 << 3)
-
-AST_APP_OPTIONS(background_opts, {
-       AST_APP_OPTION('s', BACKGROUND_SKIP),
-       AST_APP_OPTION('n', BACKGROUND_NOANSWER),
-       AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
-       AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
-});
-
-#define WAITEXTEN_MOH          (1 << 0)
-#define WAITEXTEN_DIALTONE     (1 << 1)
-
-AST_APP_OPTIONS(waitexten_opts, {
-       AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
-       AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
-});
-
 struct ast_context;
 struct ast_app;
 
@@ -853,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  */
@@ -867,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
 {
@@ -922,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. */
@@ -1000,8 +341,11 @@ struct ast_hint {
 
        char context_name[AST_MAX_CONTEXT];/*!< Context of destroyed hint extension. */
        char exten_name[AST_MAX_EXTENSION];/*!< Extension of destroyed hint extension. */
+
+       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);
@@ -1031,16 +375,43 @@ struct ast_hintdevice {
        char hintdevice[1];
 };
 
+/*! \brief Container for autohint contexts */
+static struct ao2_container *autohints;
 
 /*!
- * \note Using the device for hash
+ * \brief Structure for dial plan autohints
  */
-static int hintdevice_hash_cb(const void *obj, const int flags)
+struct ast_autohint {
+       /*! \brief Name of the registrar */
+       char *registrar;
+       /*! \brief Name of the context */
+       char context[1];
+};
+
+/*!
+ * \note Using the device for hash
+ */
+static int hintdevice_hash_cb(const void *obj, const int flags)
 {
-       const struct ast_hintdevice *ext = obj;
+       const struct ast_hintdevice *ext;
+       const char *key;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               ext = obj;
+               key = ext->hintdevice;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
 
-       return ast_str_case_hash(ext->hintdevice);
+       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
@@ -1048,29 +419,111 @@ static int hintdevice_hash_cb(const void *obj, const int flags)
  */
 static int hintdevice_cmp_multiple(void *obj, void *arg, int flags)
 {
-       struct ast_hintdevice *ext = obj, *ext2 = arg;
+       struct ast_hintdevice *left = obj;
+       struct ast_hintdevice *right = arg;
+       const char *right_key = arg;
+       int cmp;
 
-       return !strcasecmp(ext->hintdevice, ext2->hintdevice) ? CMP_MATCH  : 0;
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = right->hintdevice;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcasecmp(left->hintdevice, 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->hintdevice, right_key, strlen(right_key));
+               break;
+       default:
+               ast_assert(0);
+               cmp = 0;
+               break;
+       }
+       return cmp ? 0 : CMP_MATCH;
 }
 
-/*
- * \details This is used with ao2_callback to remove old devices
- * when they are linked to the hint
-*/
-static int hintdevice_remove_cb(void *deviceobj, void *arg, int flags)
+/*!
+ * \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)
 {
-       struct ast_hintdevice *device = deviceobj;
-       struct ast_hint *hint = arg;
+       struct ast_hintdevice *candidate = obj;
+       char *device = arg;
+       struct ast_hint *hint = data;
 
-       return (device->hint == hint) ? CMP_MATCH : 0;
+       if (!strcasecmp(candidate->hintdevice, device)
+               && candidate->hint == hint) {
+               return CMP_MATCH;
+       }
+       return 0;
 }
 
 static int remove_hintdevice(struct ast_hint *hint)
 {
-       /* iterate through all devices and remove the devices which are linked to this hint */
-       ao2_t_callback(hintdevices, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
-               hintdevice_remove_cb, hint,
-               "callback to remove all devices which are linked to a hint");
+       while (AST_VECTOR_SIZE(&hint->devices) > 0) {
+               char *device = AST_VECTOR_GET(&hint->devices, 0);
+
+               ao2_t_callback_data(hintdevices, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA,
+                       hintdevice_remove_cb, device, hint, "Remove device from container");
+               AST_VECTOR_REMOVE_UNORDERED(&hint->devices, 0);
+               ast_free(device);
+       }
+
        return 0;
 }
 
@@ -1111,18 +564,36 @@ static int add_hintdevice(struct ast_hint *hint, const char *devicelist)
                return -1;
        }
        ast_str_set(&str, 0, "%s", devicelist);
-       parse = parse_hint_device(str);
+       parse = ast_str_buffer(str);
+
+       /* Spit on '&' and ',' to handle presence hints as well */
+       while ((cur = strsep(&parse, "&,"))) {
+               char *device_name;
 
-       while ((cur = strsep(&parse, "&"))) {
                devicelength = strlen(cur);
+               if (!devicelength) {
+                       continue;
+               }
+
+               device_name = ast_strdup(cur);
+               if (!device_name) {
+                       return -1;
+               }
+
                device = ao2_t_alloc(sizeof(*device) + devicelength, hintdevice_destroy,
                        "allocating a hintdevice structure");
                if (!device) {
+                       ast_free(device_name);
                        return -1;
                }
                strcpy(device->hintdevice, cur);
                ao2_ref(hint, +1);
                device->hint = hint;
+               if (AST_VECTOR_APPEND(&hint->devices, device_name)) {
+                       ast_free(device_name);
+                       ao2_ref(device, -1);
+                       return -1;
+               }
                ao2_t_link(hintdevices, device, "Linking device into hintdevice container.");
                ao2_t_ref(device, -1, "hintdevice is linked so we can unref");
        }
@@ -1155,34 +626,10 @@ struct pbx_exception {
        int priority;                           /*!< Priority associated with this exception */
 };
 
-static int pbx_builtin_answer(struct ast_channel *, const char *);
-static int pbx_builtin_goto(struct ast_channel *, const char *);
-static int pbx_builtin_hangup(struct ast_channel *, const char *);
-static int pbx_builtin_background(struct ast_channel *, const char *);
-static int pbx_builtin_wait(struct ast_channel *, const char *);
-static int pbx_builtin_waitexten(struct ast_channel *, const char *);
-static int pbx_builtin_incomplete(struct ast_channel *, const char *);
-static int pbx_builtin_setamaflags(struct ast_channel *, const char *);
-static int pbx_builtin_ringing(struct ast_channel *, const char *);
-static int pbx_builtin_proceeding(struct ast_channel *, const char *);
-static int pbx_builtin_progress(struct ast_channel *, const char *);
-static int pbx_builtin_congestion(struct ast_channel *, const char *);
-static int pbx_builtin_busy(struct ast_channel *, const char *);
-static int pbx_builtin_noop(struct ast_channel *, const char *);
-static int pbx_builtin_gotoif(struct ast_channel *, const char *);
-static int pbx_builtin_gotoiftime(struct ast_channel *, const char *);
-static int pbx_builtin_execiftime(struct ast_channel *, const char *);
-static int pbx_builtin_saynumber(struct ast_channel *, const char *);
-static int pbx_builtin_saydigits(struct ast_channel *, const char *);
-static int pbx_builtin_saycharacters(struct ast_channel *, const char *);
-static int pbx_builtin_saycharacters_case(struct ast_channel *, const char *);
-static int pbx_builtin_sayphonetic(struct ast_channel *, const char *);
 static int matchcid(const char *cidpattern, const char *callerid);
 #ifdef NEED_DEBUG
 static void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
 #endif
-static int pbx_builtin_importvar(struct ast_channel *, const char *);
-static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
 static void new_find_extension(const char *str, struct scoreboard *score,
                struct match_char *tree, int length, int spec, const char *callerid,
                const char *label, enum ext_match_t action);
@@ -1205,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
@@ -1310,10 +759,6 @@ static unsigned int hashtab_hash_labels(const void *obj)
        return ast_hashtab_hash_string(S_OR(ac->label, ""));
 }
 
-
-AST_RWLOCK_DEFINE_STATIC(globalslock);
-static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
-
 static int autofallthrough = 1;
 static int extenpatternmatchnew = 0;
 static char *overrideswitch = NULL;
@@ -1327,50 +772,6 @@ AST_MUTEX_DEFINE_STATIC(maxcalllock);
 static int countcalls;
 static int totalcalls;
 
-/*!
- * \brief Registered functions container.
- *
- * It is sorted by function name.
- */
-static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
-
-/*! \brief Declaration of builtin applications */
-static struct pbx_builtin {
-       char name[AST_MAX_APP];
-       int (*execute)(struct ast_channel *chan, const char *data);
-} builtins[] =
-{
-       /* These applications are built into the PBX core and do not
-          need separate modules */
-
-       { "Answer",         pbx_builtin_answer },
-       { "BackGround",     pbx_builtin_background },
-       { "Busy",           pbx_builtin_busy },
-       { "Congestion",     pbx_builtin_congestion },
-       { "ExecIfTime",     pbx_builtin_execiftime },
-       { "Goto",           pbx_builtin_goto },
-       { "GotoIf",         pbx_builtin_gotoif },
-       { "GotoIfTime",     pbx_builtin_gotoiftime },
-       { "ImportVar",      pbx_builtin_importvar },
-       { "Hangup",         pbx_builtin_hangup },
-       { "Incomplete",     pbx_builtin_incomplete },
-       { "NoOp",           pbx_builtin_noop },
-       { "Proceeding",     pbx_builtin_proceeding },
-       { "Progress",       pbx_builtin_progress },
-       { "RaiseException", pbx_builtin_raise_exception },
-       { "Ringing",        pbx_builtin_ringing },
-       { "SayAlpha",       pbx_builtin_saycharacters },
-       { "SayAlphaCase",   pbx_builtin_saycharacters_case },
-       { "SayDigits",      pbx_builtin_saydigits },
-       { "SayNumber",      pbx_builtin_saynumber },
-       { "SayPhonetic",    pbx_builtin_sayphonetic },
-       { "Set",            pbx_builtin_setvar },
-       { "MSet",           pbx_builtin_setvar_multiple },
-       { "SetAMAFlags",    pbx_builtin_setamaflags },
-       { "Wait",           pbx_builtin_wait },
-       { "WaitExten",      pbx_builtin_waitexten }
-};
-
 static struct ast_context *contexts;
 static struct ast_hashtab *contexts_table = NULL;
 
@@ -1387,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
@@ -1490,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();
                                }
@@ -1585,96 +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_appl_set(c, app->name);
-       ast_channel_data_set(c, data);
-       ast_channel_publish_snapshot(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;
-}
-
-
-/*! Go no deeper than this through includes (not counting loops) */
-#define AST_PBX_MAX_STACK      128
-
-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);
@@ -1815,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);
@@ -1934,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 */           \
                                                                }                                                                                                        \
                                                        }                                                                                                            \
@@ -1947,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 */                                                 \
                                                        }                                                                                                                                                \
@@ -1991,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 */
                                        }
                                }
@@ -2013,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 */
                                        }
                                }
@@ -2029,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 */
                                        }
                                }
@@ -2382,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;
@@ -2408,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;
@@ -2815,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;
@@ -3057,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;
@@ -3064,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];
 };
@@ -3123,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;
@@ -3347,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 */
@@ -3373,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 {
@@ -3393,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
@@ -3408,291 +2764,6 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
        return NULL;
 }
 
-/*!
- * \brief extract offset:length from variable name.
- * \return 1 if there is a offset:length part, which is
- * trimmed off (values go into variables)
- */
-static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
-{
-       int parens = 0;
-
-       *offset = 0;
-       *length = INT_MAX;
-       *isfunc = 0;
-       for (; *var; var++) {
-               if (*var == '(') {
-                       (*isfunc)++;
-                       parens++;
-               } else if (*var == ')') {
-                       parens--;
-               } else if (*var == ':' && parens == 0) {
-                       *var++ = '\0';
-                       sscanf(var, "%30d:%30d", offset, length);
-                       return 1; /* offset:length valid */
-               }
-       }
-       return 0;
-}
-
-/*!
- *\brief takes a substring. It is ok to call with value == workspace.
- * \param value
- * \param offset < 0 means start from the end of the string and set the beginning
- *   to be that many characters back.
- * \param length is the length of the substring, a value less than 0 means to leave
- * that many off the end.
- * \param workspace
- * \param workspace_len
- * Always return a copy in workspace.
- */
-static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
-{
-       char *ret = workspace;
-       int lr; /* length of the input string after the copy */
-
-       ast_copy_string(workspace, value, workspace_len); /* always make a copy */
-
-       lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
-
-       /* Quick check if no need to do anything */
-       if (offset == 0 && length >= lr)        /* take the whole string */
-               return ret;
-
-       if (offset < 0) {       /* translate negative offset into positive ones */
-               offset = lr + offset;
-               if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
-                       offset = 0;
-       }
-
-       /* too large offset result in empty string so we know what to return */
-       if (offset >= lr)
-               return ret + lr;        /* the final '\0' */
-
-       ret += offset;          /* move to the start position */
-       if (length >= 0 && length < lr - offset)        /* truncate if necessary */
-               ret[length] = '\0';
-       else if (length < 0) {
-               if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
-                       ret[lr + length - offset] = '\0';
-               else
-                       ret[0] = '\0';
-       }
-
-       return ret;
-}
-
-static const char *ast_str_substring(struct ast_str *value, int offset, int length)
-{
-       int lr; /* length of the input string after the copy */
-
-       lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
-
-       /* Quick check if no need to do anything */
-       if (offset == 0 && length >= lr)        /* take the whole string */
-               return ast_str_buffer(value);
-
-       if (offset < 0) {       /* translate negative offset into positive ones */
-               offset = lr + offset;
-               if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
-                       offset = 0;
-       }
-
-       /* too large offset result in empty string so we know what to return */
-       if (offset >= lr) {
-               ast_str_reset(value);
-               return ast_str_buffer(value);
-       }
-
-       if (offset > 0) {
-               /* Go ahead and chop off the beginning */
-               memmove(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset + 1);
-               lr -= offset;
-       }
-
-       if (length >= 0 && length < lr) {       /* truncate if necessary */
-               char *tmp = ast_str_buffer(value);
-               tmp[length] = '\0';
-               ast_str_update(value);
-       } else if (length < 0) {
-               if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
-                       char *tmp = ast_str_buffer(value);
-                       tmp[lr + length] = '\0';
-                       ast_str_update(value);
-               } else {
-                       ast_str_reset(value);
-               }
-       } else {
-               /* Nothing to do, but update the buffer length */
-               ast_str_update(value);
-       }
-
-       return ast_str_buffer(value);
-}
-
-/*! \brief  Support for Asterisk built-in variables in the dialplan
-
-\note  See also
-       - \ref AstVar   Channel variables
-       - \ref AstCauses The HANGUPCAUSE variable
- */
-void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
-{
-       struct ast_str *str = ast_str_create(16);
-       const char *cret;
-
-       cret = ast_str_retrieve_variable(&str, 0, c, headp, var);
-       ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
-       *ret = cret ? workspace : NULL;
-       ast_free(str);
-}
-
-const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
-{
-       const char not_found = '\0';
-       char *tmpvar;
-       const char *ret;
-       const char *s;  /* the result */
-       int offset, length;
-       int i, need_substring;
-       struct varshead *places[2] = { headp, &globals };       /* list of places where we may look */
-       char workspace[20];
-
-       if (c) {
-               ast_channel_lock(c);
-               places[0] = ast_channel_varshead(c);
-       }
-       /*
-        * Make a copy of var because parse_variable_name() modifies the string.
-        * Then if called directly, we might need to run substring() on the result;
-        * remember this for later in 'need_substring', 'offset' and 'length'
-        */
-       tmpvar = ast_strdupa(var);      /* parse_variable_name modifies the string */
-       need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
-
-       /*
-        * Look first into predefined variables, then into variable lists.
-        * Variable 's' points to the result, according to the following rules:
-        * s == &not_found (set at the beginning) means that we did not find a
-        *      matching variable and need to look into more places.
-        * If s != &not_found, s is a valid result string as follows:
-        * s = NULL if the variable does not have a value;
-        *      you typically do this when looking for an unset predefined variable.
-        * s = workspace if the result has been assembled there;
-        *      typically done when the result is built e.g. with an snprintf(),
-        *      so we don't need to do an additional copy.
-        * s != workspace in case we have a string, that needs to be copied
-        *      (the ast_copy_string is done once for all at the end).
-        *      Typically done when the result is already available in some string.
-        */
-       s = &not_found; /* default value */
-       if (c) {        /* This group requires a valid channel */
-               /* Names with common parts are looked up a piece at a time using strncmp. */
-               if (!strncmp(var, "CALL", 4)) {
-                       if (!strncmp(var + 4, "ING", 3)) {
-                               if (!strcmp(var + 7, "PRES")) {                 /* CALLINGPRES */
-                                       ast_str_set(str, maxlen, "%d",
-                                               ast_party_id_presentation(&ast_channel_caller(c)->id));
-                                       s = ast_str_buffer(*str);
-                               } else if (!strcmp(var + 7, "ANI2")) {          /* CALLINGANI2 */
-                                       ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->ani2);
-                                       s = ast_str_buffer(*str);
-                               } else if (!strcmp(var + 7, "TON")) {           /* CALLINGTON */
-                                       ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->id.number.plan);
-                                       s = ast_str_buffer(*str);
-                               } else if (!strcmp(var + 7, "TNS")) {           /* CALLINGTNS */
-                                       ast_str_set(str, maxlen, "%d", ast_channel_dialed(c)->transit_network_select);
-                                       s = ast_str_buffer(*str);
-                               }
-                       }
-               } else if (!strcmp(var, "HINT")) {
-                       s = ast_str_get_hint(str, maxlen, NULL, 0, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
-               } else if (!strcmp(var, "HINTNAME")) {
-                       s = ast_str_get_hint(NULL, 0, str, maxlen, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
-               } else if (!strcmp(var, "EXTEN")) {
-                       s = ast_channel_exten(c);
-               } else if (!strcmp(var, "CONTEXT")) {
-                       s = ast_channel_context(c);
-               } else if (!strcmp(var, "PRIORITY")) {
-                       ast_str_set(str, maxlen, "%d", ast_channel_priority(c));
-                       s = ast_str_buffer(*str);
-               } else if (!strcmp(var, "CHANNEL")) {
-                       s = ast_channel_name(c);
-               } else if (!strcmp(var, "UNIQUEID")) {
-                       s = ast_channel_uniqueid(c);
-               } else if (!strcmp(var, "HANGUPCAUSE")) {
-                       ast_str_set(str, maxlen, "%d", ast_channel_hangupcause(c));
-                       s = ast_str_buffer(*str);
-               }
-       }
-       if (s == &not_found) { /* look for more */
-               if (!strcmp(var, "EPOCH")) {
-                       ast_str_set(str, maxlen, "%u", (int) time(NULL));
-                       s = ast_str_buffer(*str);
-               } else if (!strcmp(var, "SYSTEMNAME")) {
-                       s = ast_config_AST_SYSTEM_NAME;
-               } else if (!strcmp(var, "ASTETCDIR")) {
-                       s = ast_config_AST_CONFIG_DIR;
-               } else if (!strcmp(var, "ASTMODDIR")) {
-                       s = ast_config_AST_MODULE_DIR;
-               } else if (!strcmp(var, "ASTVARLIBDIR")) {
-                       s = ast_config_AST_VAR_DIR;
-               } else if (!strcmp(var, "ASTDBDIR")) {
-                       s = ast_config_AST_DB;
-               } else if (!strcmp(var, "ASTKEYDIR")) {
-                       s = ast_config_AST_KEY_DIR;
-               } else if (!strcmp(var, "ASTDATADIR")) {
-                       s = ast_config_AST_DATA_DIR;
-               } else if (!strcmp(var, "ASTAGIDIR")) {
-                       s = ast_config_AST_AGI_DIR;
-               } else if (!strcmp(var, "ASTSPOOLDIR")) {
-                       s = ast_config_AST_SPOOL_DIR;
-               } else if (!strcmp(var, "ASTRUNDIR")) {
-                       s = ast_config_AST_RUN_DIR;
-               } else if (!strcmp(var, "ASTLOGDIR")) {
-                       s = ast_config_AST_LOG_DIR;
-               } else if (!strcmp(var, "ENTITYID")) {
-                       ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
-                       s = workspace;
-               }
-       }
-       /* if not found, look into chanvars or global vars */
-       for (i = 0; s == &not_found && i < ARRAY_LEN(places); i++) {
-               struct ast_var_t *variables;
-               if (!places[i])
-                       continue;
-               if (places[i] == &globals)
-                       ast_rwlock_rdlock(&globalslock);
-               AST_LIST_TRAVERSE(places[i], variables, entries) {
-                       if (!strcmp(ast_var_name(variables), var)) {
-                               s = ast_var_value(variables);
-                               break;
-                       }
-               }
-               if (places[i] == &globals)
-                       ast_rwlock_unlock(&globalslock);
-       }
-       if (s == &not_found || s == NULL) {
-               ast_debug(5, "Result of '%s' is NULL\n", var);
-               ret = NULL;
-       } else {
-               ast_debug(5, "Result of '%s' is '%s'\n", var, s);
-               if (s != ast_str_buffer(*str)) {
-                       ast_str_set(str, maxlen, "%s", s);
-               }
-               ret = ast_str_buffer(*str);
-               if (need_substring) {
-                       ret = ast_str_substring(*str, offset, length);
-                       ast_debug(2, "Final result of '%s' is '%s'\n", var, ret);
-               }
-       }
-
-       if (c) {
-               ast_channel_unlock(c);
-       }
-       return ret;
-}
-
 static void exception_store_free(void *data)
 {
        struct pbx_exception *exception = data;
@@ -3716,7 +2787,7 @@ static const struct ast_datastore_info exception_store_info = {
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int raise_exception(struct ast_channel *chan, const char *reason, int priority)
+int raise_exception(struct ast_channel *chan, const char *reason, int priority)
 {
        struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
        struct pbx_exception *exception = NULL;
@@ -3742,12 +2813,6 @@ static int raise_exception(struct ast_channel *chan, const char *reason, int pri
        return 0;
 }
 
-int pbx_builtin_raise_exception(struct ast_channel *chan, const char *reason)
-{
-       /* Priority will become 1, next time through the AUTOLOOP */
-       return raise_exception(chan, reason, 0);
-}
-
 static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
 {
        struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
@@ -3773,1468 +2838,1038 @@ static struct ast_custom_function exception_function = {
        .read = acf_exception_read,
 };
 
-static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+/*!
+ * \brief The return value depends on the action:
+ *
+ * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
+ *     and return 0 on failure, -1 on match;
+ * E_FINDLABEL maps the label to a priority, and returns
+ *     the priority on success, ... XXX
+ * E_SPAWN, spawn an application,
+ *
+ * \retval 0 on success.
+ * \retval  -1 on failure.
+ *
+ * \note The channel is auto-serviced in this function, because doing an extension
+ * match may block for a long time.  For example, if the lookup has to use a network
+ * dialplan switch, such as DUNDi or IAX2, it may take a while.  However, the channel
+ * auto-service code will queue up any important signalling frames to be processed
+ * after this is done.
+ */
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
+  const char *context, const char *exten, int priority,
+  const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
 {
-       struct ast_custom_function *acf;
-       int count_acf = 0;
-       int like = 0;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show functions [like]";
-               e->usage =
-                       "Usage: core show functions [like <text>]\n"
-                       "       List builtin functions, optionally only those matching a given string\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
-               like = 1;
-       } else if (a->argc != 3) {
-               return CLI_SHOWUSAGE;
-       }
-
-       ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
-
-       AST_RWLIST_RDLOCK(&acf_root);
-       AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
-               if (!like || strstr(acf->name, a->argv[4])) {
-                       count_acf++;
-                       ast_cli(a->fd, "%-20.20s  %-35.35s  %s\n",
-                               S_OR(acf->name, ""),
-                               S_OR(acf->syntax, ""),
-                               S_OR(acf->synopsis, ""));
-               }
-       }
-       AST_RWLIST_UNLOCK(&acf_root);
-
-       ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+       struct ast_exten *e;
+       struct ast_app *app;
+       char *substitute = NULL;
+       int res;
+       struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+       char passdata[EXT_DATA_SIZE];
+       int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
 
-       return CLI_SUCCESS;
-}
+       ast_rdlock_contexts();
+       if (found)
+               *found = 0;
 
-static char *complete_functions(const char *word, int pos, int state)
-{
-       struct ast_custom_function *cur;
-       char *ret = NULL;
-       int which = 0;
-       int wordlen;
-       int cmp;
-
-       if (pos != 3) {
-               return NULL;
-       }
-
-       wordlen = strlen(word);
-       AST_RWLIST_RDLOCK(&acf_root);
-       AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
-               /*
-                * Do a case-insensitive search for convenience in this
-                * 'complete' function.
-                *
-                * We must search the entire container because the functions are
-                * sorted and normally found case sensitively.
-                */
-               cmp = strncasecmp(word, cur->name, wordlen);
-               if (!cmp) {
-                       /* Found match. */
-                       if (++which <= state) {
-                               /* Not enough matches. */
-                               continue;
+       e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
+       if (e) {
+               if (found)
+                       *found = 1;
+               if (matching_action) {
+                       ast_unlock_contexts();
+                       return -1;      /* success, we found it */
+               } else if (action == E_FINDLABEL) { /* map the label to a priority */
+                       res = e->priority;
+                       ast_unlock_contexts();
+                       return res;     /* the priority we were looking for */
+               } else {        /* spawn */
+                       if (!e->cached_app)
+                               e->cached_app = pbx_findapp(e->app);
+                       app = e->cached_app;
+                       if (ast_strlen_zero(e->data)) {
+                               *passdata = '\0';
+                       } else {
+                               const char *tmp;
+                               if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
+                                       /* no variables to substitute, copy on through */
+                                       ast_copy_string(passdata, e->data, sizeof(passdata));
+                               } else {
+                                       /* save e->data on stack for later processing after lock released */
+                                       substitute = ast_strdupa(e->data);
+                               }
                        }
-                       ret = ast_strdup(cur->name);
+                       ast_unlock_contexts();
+                       if (!app) {
+                               ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
+                               return -1;
+                       }
+                       if (ast_channel_context(c) != context)
+                               ast_channel_context_set(c, context);
+                       if (ast_channel_exten(c) != exten)
+                               ast_channel_exten_set(c, exten);
+                       ast_channel_priority_set(c, priority);
+                       if (substitute) {
+                               pbx_substitute_variables_helper(c, substitute, passdata, sizeof(passdata)-1);
+                       }
+                       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(app)),
+                                       COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(c)),
+                                       COLORIZE(COLOR_BRMAGENTA, 0, passdata),
+                                       "in new stack");
+                       }
+                       return pbx_exec(c, app, passdata);      /* 0 on success, -1 on failure */
+               }
+       } else if (q.swo) {     /* not found here, but in another switch */
+               if (found)
+                       *found = 1;
+               ast_unlock_contexts();
+               if (matching_action) {
+                       return -1;
+               } else {
+                       if (!q.swo->exec) {
+                               ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
+                               res = -1;
+                       }
+                       return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
+               }
+       } else {        /* not found anywhere, see what happened */
+               ast_unlock_contexts();
+               /* Using S_OR here because Solaris doesn't like NULL being passed to ast_log */
+               switch (q.status) {
+               case STATUS_NO_CONTEXT:
+                       if (!matching_action && !combined_find_spawn)
+                               ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", S_OR(context, ""));
+                       break;
+               case STATUS_NO_EXTENSION:
+                       if (!matching_action && !combined_find_spawn)
+                               ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, S_OR(context, ""));
+                       break;
+               case STATUS_NO_PRIORITY:
+                       if (!matching_action && !combined_find_spawn)
+                               ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, S_OR(context, ""));
+                       break;
+               case STATUS_NO_LABEL:
+                       if (context && !combined_find_spawn)
+                               ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, S_OR(context, ""));
                        break;
+               default:
+                       ast_debug(1, "Shouldn't happen!\n");
                }
-       }
-       AST_RWLIST_UNLOCK(&acf_root);
 
-       return ret;
+               return (matching_action) ? 0 : -1;
+       }
 }
 
-static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+/*! \brief Find hint for given extension in context */
+static struct ast_exten *ast_hint_extension_nolock(struct ast_channel *c, const char *context, const char *exten)
 {
-       struct ast_custom_function *acf;
-       /* Maximum number of characters added by terminal coloring is 22 */
-       char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40], argtitle[40], seealsotitle[40];
-       char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL, *seealso = NULL;
-       char stxtitle[40], *syntax = NULL, *arguments = NULL;
-       int syntax_size, description_size, synopsis_size, arguments_size, seealso_size;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show function";
-               e->usage =
-                       "Usage: core show function <function>\n"
-                       "       Describe a particular dialplan function.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return complete_functions(a->word, a->pos, a->n);
-       }
-
-       if (a->argc != 4) {
-               return CLI_SHOWUSAGE;
-       }
+       struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
+       return pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
+}
 
-       if (!(acf = ast_custom_function_find(a->argv[3]))) {
-               ast_cli(a->fd, "No function by that name registered.\n");
-               return CLI_FAILURE;
-       }
+static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
+{
+       struct ast_exten *e;
+       ast_rdlock_contexts();
+       e = ast_hint_extension_nolock(c, context, exten);
+       ast_unlock_contexts();
+       return e;
+}
 
-       syntax_size = strlen(S_OR(acf->syntax, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-       if (!(syntax = ast_malloc(syntax_size))) {
-               ast_cli(a->fd, "Memory allocation failure!\n");
-               return CLI_FAILURE;
+enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
+{
+       switch (devstate) {
+       case AST_DEVICE_ONHOLD:
+               return AST_EXTENSION_ONHOLD;
+       case AST_DEVICE_BUSY:
+               return AST_EXTENSION_BUSY;
+       case AST_DEVICE_UNKNOWN:
+               return AST_EXTENSION_NOT_INUSE;
+       case AST_DEVICE_UNAVAILABLE:
+       case AST_DEVICE_INVALID:
+               return AST_EXTENSION_UNAVAILABLE;
+       case AST_DEVICE_RINGINUSE:
+               return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
+       case AST_DEVICE_RINGING:
+               return AST_EXTENSION_RINGING;
+       case AST_DEVICE_INUSE:
+               return AST_EXTENSION_INUSE;
+       case AST_DEVICE_NOT_INUSE:
+               return AST_EXTENSION_NOT_INUSE;
+       case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
+               break;
        }
 
-       snprintf(info, sizeof(info), "\n  -= Info about function '%s' =- \n\n", acf->name);
-       term_color(infotitle, info, COLOR_MAGENTA, 0, sizeof(infotitle));
-       term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
-       term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
-       term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
-       term_color(argtitle, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
-       term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, 40);
-       term_color(syntax, S_OR(acf->syntax, "Not available"), COLOR_CYAN, 0, syntax_size);
-#ifdef AST_XML_DOCS
-       if (acf->docsrc == AST_XML_DOC) {
-               arguments = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1);
-               synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1);
-               description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1);
-               seealso = ast_xmldoc_printable(S_OR(acf->seealso, "Not available"), 1);
-       } else
-#endif
-       {
-               synopsis_size = strlen(S_OR(acf->synopsis, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               synopsis = ast_malloc(synopsis_size);
-
-               description_size = strlen(S_OR(acf->desc, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               description = ast_malloc(description_size);
-
-               arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               arguments = ast_malloc(arguments_size);
-
-               seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               seealso = ast_malloc(seealso_size);
+       return AST_EXTENSION_NOT_INUSE;
+}
 
-               /* check allocated memory. */
-               if (!synopsis || !description || !arguments || !seealso) {
-                       ast_free(synopsis);
-                       ast_free(description);
-                       ast_free(arguments);
-                       ast_free(seealso);
-                       ast_free(syntax);
-                       return CLI_FAILURE;
-               }
+/*!
+ * \internal
+ * \brief Parse out the presence portion of the hint string
+ */
+static char *parse_hint_presence(struct ast_str *hint_args)
+{
+       char *copy = ast_strdupa(ast_str_buffer(hint_args));
+       char *tmp = "";
 
-               term_color(arguments, S_OR(acf->arguments, "Not available"), COLOR_CYAN, 0, arguments_size);
-               term_color(synopsis, S_OR(acf->synopsis, "Not available"), COLOR_CYAN, 0, synopsis_size);
-               term_color(description, S_OR(acf->desc, "Not available"), COLOR_CYAN, 0, description_size);
-               term_color(seealso, S_OR(acf->seealso, "Not available"), COLOR_CYAN, 0, seealso_size);
+       if ((tmp = strrchr(copy, ','))) {
+               *tmp = '\0';
+               tmp++;
+       } else {
+               return NULL;
        }
-
-       ast_cli(a->fd, "%s%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n",
-                       infotitle, syntitle, synopsis, destitle, description,
-                       stxtitle, syntax, argtitle, arguments, seealsotitle, seealso);
-
-       ast_free(arguments);
-       ast_free(synopsis);
-       ast_free(description);
-       ast_free(seealso);
-       ast_free(syntax);
-
-       return CLI_SUCCESS;
+       ast_str_set(&hint_args, 0, "%s", tmp);
+       return ast_str_buffer(hint_args);
 }
 
-static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
+/*!
+ * \internal
+ * \brief Parse out the device portion of the hint string
+ */
+static char *parse_hint_device(struct ast_str *hint_args)
 {
-       struct ast_custom_function *cur;
-       int cmp;
+       char *copy = ast_strdupa(ast_str_buffer(hint_args));
+       char *tmp;
 
-       AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
-               cmp = strcmp(name, cur->name);
-               if (cmp > 0) {
-                       continue;
-               }
-               if (!cmp) {
-                       /* Found it. */
-                       break;
-               }
-               /* Not in container. */
-               cur = NULL;
-               break;
+       if ((tmp = strrchr(copy, ','))) {
+               *tmp = '\0';
        }
 
-       return cur;
+       ast_str_set(&hint_args, 0, "%s", copy);
+       return ast_str_buffer(hint_args);
 }
 
-struct ast_custom_function *ast_custom_function_find(const char *name)
+static void device_state_info_dt(void *obj)
 {
-       struct ast_custom_function *acf;
+       struct ast_device_state_info *info = obj;
 
-       AST_RWLIST_RDLOCK(&acf_root);
-       acf = ast_custom_function_find_nolock(name);
-       AST_RWLIST_UNLOCK(&acf_root);
+       ao2_cleanup(info->causing_channel);
+}
 
-       return acf;
+static struct ao2_container *alloc_device_state_info(void)
+{
+       return ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
 }
 
-int ast_custom_function_unregister(struct ast_custom_function *acf)
+static int ast_extension_state3(struct ast_str *hint_app, struct ao2_container *device_state_info)
 {
-       struct ast_custom_function *cur;
+       char *cur;
+       char *rest;
+       struct ast_devstate_aggregate agg;
 
-       if (!acf) {
-               return -1;
-       }
+       /* One or more devices separated with a & character */
+       rest = parse_hint_device(hint_app);
+
+       ast_devstate_aggregate_init(&agg);
+       while ((cur = strsep(&rest, "&"))) {
+               enum ast_device_state state = ast_device_state(cur);
+
+               ast_devstate_aggregate_add(&agg, state);
+               if (device_state_info) {
+                       struct ast_device_state_info *obj;
 
-       AST_RWLIST_WRLOCK(&acf_root);
-       if ((cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist))) {
-#ifdef AST_XML_DOCS
-               if (cur->docsrc == AST_XML_DOC) {
-                       ast_string_field_free_memory(acf);
+                       obj = ao2_alloc_options(sizeof(*obj) + strlen(cur), device_state_info_dt, AO2_ALLOC_OPT_LOCK_NOLOCK);
+                       /* if failed we cannot add this device */
+                       if (obj) {
+                               obj->device_state = state;
+                               strcpy(obj->device_name, cur);
+                               ao2_link(device_state_info, obj);
+                               ao2_ref(obj, -1);
+                       }
                }
-#endif
-               ast_verb(2, "Unregistered custom function %s\n", cur->name);
        }
-       AST_RWLIST_UNLOCK(&acf_root);
 
-       return cur ? 0 : -1;
+       return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
 }
 
-/*! \internal
- *  \brief Retrieve the XML documentation of a specified ast_custom_function,
- *         and populate ast_custom_function string fields.
- *  \param acf ast_custom_function structure with empty 'desc' and 'synopsis'
- *             but with a function 'name'.
- *  \retval -1 On error.
- *  \retval 0 On succes.
- */
-static int acf_retrieve_docs(struct ast_custom_function *acf)
+/*! \brief Check state of extension by using hints */
+static int ast_extension_state2(struct ast_exten *e, struct ao2_container *device_state_info)
 {
-#ifdef AST_XML_DOCS
-       char *tmpxml;
-
-       /* Let's try to find it in the Documentation XML */
-       if (!ast_strlen_zero(acf->desc) || !ast_strlen_zero(acf->synopsis)) {
-               return 0;
-       }
+       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
 
-       if (ast_string_field_init(acf, 128)) {
+       if (!e || !hint_app) {
                return -1;
        }
 
-       /* load synopsis */
-       tmpxml = ast_xmldoc_build_synopsis("function", acf->name, ast_module_name(acf->mod));
-       ast_string_field_set(acf, synopsis, tmpxml);
-       ast_free(tmpxml);
-
-       /* load description */
-       tmpxml = ast_xmldoc_build_description("function", acf->name, ast_module_name(acf->mod));
-       ast_string_field_set(acf, desc, tmpxml);
-       ast_free(tmpxml);
-
-       /* load syntax */
-       tmpxml = ast_xmldoc_build_syntax("function", acf->name, ast_module_name(acf->mod));
-       ast_string_field_set(acf, syntax, tmpxml);
-       ast_free(tmpxml);
-
-       /* load arguments */
-       tmpxml = ast_xmldoc_build_arguments("function", acf->name, ast_module_name(acf->mod));
-       ast_string_field_set(acf, arguments, tmpxml);
-       ast_free(tmpxml);
-
-       /* load seealso */
-       tmpxml = ast_xmldoc_build_seealso("function", acf->name, ast_module_name(acf->mod));
-       ast_string_field_set(acf, seealso, tmpxml);
-       ast_free(tmpxml);
-
-       acf->docsrc = AST_XML_DOC;
-#endif
-
-       return 0;
+       ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
+       return ast_extension_state3(hint_app, device_state_info);
 }
 
-int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
+/*! \brief Return extension_state as string */
+const char *ast_extension_state2str(int extension_state)
 {
-       struct ast_custom_function *cur;
+       int i;
 
-       if (!acf) {
-               return -1;
+       for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
+               if (extension_states[i].extension_state == extension_state)
+                       return extension_states[i].text;
        }
+       return "Unknown";
+}
 
-       acf->mod = mod;
-#ifdef AST_XML_DOCS
-       acf->docsrc = AST_STATIC_DOC;
-#endif
+/*!
+ * \internal
+ * \brief Check extension state for an extension by using hint
+ */
+static int internal_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
+       struct ao2_container *device_state_info)
+{
+       struct ast_exten *e;
 
-       if (acf_retrieve_docs(acf)) {
-               return -1;
+       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
+               return -1;                   /* No hint, return -1 */
        }
 
-       AST_RWLIST_WRLOCK(&acf_root);
-
-       cur = ast_custom_function_find_nolock(acf->name);
-       if (cur) {
-               ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
-               AST_RWLIST_UNLOCK(&acf_root);
-               return -1;
-       }
-
-       /* Store in alphabetical order */
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
-               if (strcmp(acf->name, cur->name) < 0) {
-                       AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
-                       break;
+       if (e->exten[0] == '_') {
+               /* Create this hint on-the-fly */
+               ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
+                       e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
+                       e->registrar);
+               if (!(e = ast_hint_extension(c, context, exten))) {
+                       /* Improbable, but not impossible */
+                       return -1;
                }
        }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       if (!cur) {
-               AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
-       }
 
-       AST_RWLIST_UNLOCK(&acf_root);
-
-       ast_verb(2, "Registered custom function '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, acf->name));
-
-       return 0;
+       return ast_extension_state2(e, device_state_info);  /* Check all devices in the hint */
 }
 
-/*! \brief return a pointer to the arguments of the function,
- * and terminates the function name with '\\0'
- */
-static char *func_args(char *function)
+/*! \brief Check extension state for an extension by using hint */
+int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
 {
-       char *args = strchr(function, '(');
-
-       if (!args) {
-               ast_log(LOG_WARNING, "Function '%s' doesn't contain parentheses.  Assuming null argument.\n", function);
-       } else {
-               char *p;
-               *args++ = '\0';
-               if ((p = strrchr(args, ')'))) {
-                       *p = '\0';
-               } else {
-                       ast_log(LOG_WARNING, "Can't find trailing parenthesis for function '%s(%s'?\n", function, args);
-               }
-       }
-       return args;
+       return internal_extension_state_extended(c, context, exten, NULL);
 }
 
-int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+/*! \brief Check extended extension state for an extension by using hint */
+int ast_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
+       struct ao2_container **device_state_info)
 {
-       char *copy = ast_strdupa(function);
-       char *args = func_args(copy);
-       struct ast_custom_function *acfptr = ast_custom_function_find(copy);
-       int res;
-       struct ast_module_user *u = NULL;
+       struct ao2_container *container = NULL;
+       int ret;
 
-       if (acfptr == NULL) {
-               ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-       } else if (!acfptr->read && !acfptr->read2) {
-               ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
-       } else if (acfptr->read) {
-               if (acfptr->mod) {
-                       u = __ast_module_user_add(acfptr->mod, chan);
-               }
-               res = acfptr->read(chan, copy, args, workspace, len);
-               if (acfptr->mod && u) {
-                       __ast_module_user_remove(acfptr->mod, u);
-               }
-               return res;
-       } else {
-               struct ast_str *str = ast_str_create(16);
-               if (acfptr->mod) {
-                       u = __ast_module_user_add(acfptr->mod, chan);
-               }
-               res = acfptr->read2(chan, copy, args, &str, 0);
-               if (acfptr->mod && u) {
-                       __ast_module_user_remove(acfptr->mod, u);
-               }
-               ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
-               ast_free(str);
-               return res;
+       if (device_state_info) {
+               container = alloc_device_state_info();
        }
-       return -1;
-}
-
-int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
-{
-       char *copy = ast_strdupa(function);
-       char *args = func_args(copy);
-       struct ast_custom_function *acfptr = ast_custom_function_find(copy);
-       int res;
-       struct ast_module_user *u = NULL;
 
-       if (acfptr == NULL) {
-               ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-       } else if (!acfptr->read && !acfptr->read2) {
-               ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
-       } else {
-               if (acfptr->mod) {
-                       u = __ast_module_user_add(acfptr->mod, chan);
-               }
-               ast_str_reset(*str);
-               if (acfptr->read2) {
-                       /* ast_str enabled */
-                       res = acfptr->read2(chan, copy, args, str, maxlen);
-               } else {
-                       /* Legacy function pointer, allocate buffer for result */
-                       int maxsize = ast_str_size(*str);
-                       if (maxlen > -1) {
-                               if (maxlen == 0) {
-                                       if (acfptr->read_max) {
-                                               maxsize = acfptr->read_max;
-                                       } else {
-                                               maxsize = VAR_BUF_SIZE;
-                                       }
-                               } else {
-                                       maxsize = maxlen;
-                               }
-                               ast_str_make_space(str, maxsize);
-                       }
-                       res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
-               }
-               if (acfptr->mod && u) {
-                       __ast_module_user_remove(acfptr->mod, u);
-               }
-               return res;
+       ret = internal_extension_state_extended(c, context, exten, container);
+       if (ret < 0 && container) {
+               ao2_ref(container, -1);
+               container = NULL;
        }
-       return -1;
-}
-
-int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
-{
-       char *copy = ast_strdupa(function);
-       char *args = func_args(copy);
-       struct ast_custom_function *acfptr = ast_custom_function_find(copy);
 
-       if (acfptr == NULL)
-               ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-       else if (!acfptr->write)
-               ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
-       else {
-               int res;
-               struct ast_module_user *u = NULL;
-               if (acfptr->mod)
-                       u = __ast_module_user_add(acfptr->mod, chan);
-               res = acfptr->write(chan, copy, args, value);
-               if (acfptr->mod && u)
-                       __ast_module_user_remove(acfptr->mod, u);
-               return res;
+       if (device_state_info) {
+               get_device_state_causing_channels(container);
+               *device_state_info = container;
        }
 
-       return -1;
+       return ret;
 }
 
-void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
-{
-       /* Substitutes variables into buf, based on string templ */
-       char *cp4 = NULL;
-       const char *whereweare;
-       int orig_size = 0;
-       int offset, offset2, isfunction;
-       const char *nextvar, *nextexp, *nextthing;
-       const char *vars, *vare;
-       char *finalvars;
-       int pos, brackets, needsub, len;
-       struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);
-
-       ast_str_reset(*buf);
-       whereweare = templ;
-       while (!ast_strlen_zero(whereweare)) {
-               /* reset our buffer */
-               ast_str_reset(substr3);
-
-               /* Assume we're copying the whole remaining string */
-               pos = strlen(whereweare);
-               nextvar = NULL;
-               nextexp = NULL;
-               nextthing = strchr(whereweare, '$');
-               if (nextthing) {
-                       switch (nextthing[1]) {
-                       case '{':
-                               nextvar = nextthing;
-                               pos = nextvar - whereweare;
-                               break;
-                       case '[':
-                               nextexp = nextthing;
-                               pos = nextexp - whereweare;
-                               break;
-                       default:
-                               pos = 1;
-                       }
-               }
-
-               if (pos) {
-                       /* Copy that many bytes */
-                       ast_str_append_substr(buf, maxlen, whereweare, pos);
-
-                       templ += pos;
-                       whereweare += pos;
-               }
-
-               if (nextvar) {
-                       /* We have a variable.  Find the start and end, and determine
-                          if we are going to have to recursively call ourselves on the
-                          contents */
-                       vars = vare = nextvar + 2;
-                       brackets = 1;
-                       needsub = 0;
-
-                       /* Find the end of it */
-                       while (brackets && *vare) {
-                               if ((vare[0] == '$') && (vare[1] == '{')) {
-                                       needsub++;
-                               } else if (vare[0] == '{') {
-                                       brackets++;
-                               } else if (vare[0] == '}') {
-                                       brackets--;
-                               } else if ((vare[0] == '$') && (vare[1] == '['))
-                                       needsub++;
-                               vare++;
-                       }
-                       if (brackets)
-                               ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
-                       len = vare - vars - 1;
-
-                       /* Skip totally over variable string */
-                       whereweare += (len + 3);
-
-                       /* Store variable name (and truncate) */
-                       ast_str_set_substr(&substr1, 0, vars, len);
-                       ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);
-
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
-
-                               if (!substr2) {
-                                       substr2 = ast_str_create(16);
-                               }
-                               ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
-                               finalvars = ast_str_buffer(substr2);
-                       } else {
-                               finalvars = ast_str_buffer(substr1);
-                       }
-
-                       parse_variable_name(finalvars, &offset, &offset2, &isfunction);
-                       if (isfunction) {
-                               /* Evaluate function */
-                               if (c || !headp) {
-                                       cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
-                               } else {
-                                       struct varshead old;
-                                       struct ast_channel *bogus = ast_dummy_channel_alloc();
-                                       if (bogus) {
-                                               memcpy(&old, ast_channel_varshead(bogus), sizeof(old));
-                                               memcpy(ast_channel_varshead(bogus), headp, sizeof(*ast_channel_varshead(bogus)));
-                                               cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
-                                               /* Don't deallocate the varshead that was passed in */
-                                               memcpy(ast_channel_varshead(bogus), &old, sizeof(*ast_channel_varshead(bogus)));
-                                               ast_channel_unref(bogus);
-                                       } else {
-                                               ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
-                                       }
-                               }
-                               ast_debug(2, "Function %s result is '%s'\n", finalvars, cp4 ? cp4 : "(null)");
-                       } else {
-                               /* Retrieve variable value */
-                               ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);
-                               cp4 = ast_str_buffer(substr3);
-                       }
-                       if (cp4) {
-                               ast_str_substring(substr3, offset, offset2);
-                               ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
-                       }
-               } else if (nextexp) {
-                       /* We have an expression.  Find the start and end, and determine
-                          if we are going to have to recursively call ourselves on the
-                          contents */
-                       vars = vare = nextexp + 2;
-                       brackets = 1;
-                       needsub = 0;
-
-                       /* Find the end of it */
-                       while (brackets && *vare) {
-                               if ((vare[0] == '$') && (vare[1] == '[')) {
-                                       needsub++;
-                                       brackets++;
-                                       vare++;
-                               } else if (vare[0] == '[') {
-                                       brackets++;
-                               } else if (vare[0] == ']') {
-                                       brackets--;
-                               } else if ((vare[0] == '$') && (vare[1] == '{')) {
-                                       needsub++;
-                                       vare++;
-                               }
-                               vare++;
-                       }
-                       if (brackets)
-                               ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
-                       len = vare - vars - 1;
-
-                       /* Skip totally over expression */
-                       whereweare += (len + 3);
+static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
+{
+       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+       char *presence_provider;
+       const char *app;
 
-                       /* Store variable name (and truncate) */
-                       ast_str_set_substr(&substr1, 0, vars, len);
+       if (!e || !hint_app) {
+               return -1;
+       }
 
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
+       app = ast_get_extension_app(e);
+       if (ast_strlen_zero(app)) {
+               return -1;
+       }
 
-                               if (!substr2) {
-                                       substr2 = ast_str_create(16);
-                               }
-                               ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
-                               finalvars = ast_str_buffer(substr2);
-                       } else {
-                               finalvars = ast_str_buffer(substr1);
-                       }
+       ast_str_set(&hint_app, 0, "%s", app);
+       presence_provider = parse_hint_presence(hint_app);
 
-                       if (ast_str_expr(&substr3, 0, c, finalvars)) {
-                               ast_debug(2, "Expression result is '%s'\n", ast_str_buffer(substr3));
-                       }
-                       ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
-               }
+       if (ast_strlen_zero(presence_provider)) {
+               /* No presence string in the hint */
+               return 0;
        }
-       *used = ast_str_strlen(*buf) - orig_size;
-       ast_free(substr1);
-       ast_free(substr2);
-       ast_free(substr3);
-}
-
-void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
-{
-       size_t used;
-       ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);
-}
 
-void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
-{
-       size_t used;
-       ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);
+       return ast_presence_state(presence_provider, subtype, message);
 }
 
-void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
+int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
 {
-       /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!!  */
-       char *cp4 = NULL;
-       const char *whereweare, *orig_cp2 = cp2;
-       int length, offset, offset2, isfunction;
-       char *workspace = NULL;
-       char *ltmp = NULL, *var = NULL;
-       char *nextvar, *nextexp, *nextthing;
-       char *vars, *vare;
-       int pos, brackets, needsub, len;
-
-       *cp2 = 0; /* just in case nothing ends up there */
-       whereweare = cp1;
-       while (!ast_strlen_zero(whereweare) && count) {
-               /* Assume we're copying the whole remaining string */
-               pos = strlen(whereweare);
-               nextvar = NULL;
-               nextexp = NULL;
-               nextthing = strchr(whereweare, '$');
-               if (nextthing) {
-                       switch (nextthing[1]) {
-                       case '{':
-                               nextvar = nextthing;
-                               pos = nextvar - whereweare;
-                               break;
-                       case '[':
-                               nextexp = nextthing;
-                               pos = nextexp - whereweare;
-                               break;
-                       default:
-                               pos = 1;
-                       }
-               }
-
-               if (pos) {
-                       /* Can't copy more than 'count' bytes */
-                       if (pos > count)
-                               pos = count;
-
-                       /* Copy that many bytes */
-                       memcpy(cp2, whereweare, pos);
-
-                       count -= pos;
-                       cp2 += pos;
-                       whereweare += pos;
-                       *cp2 = 0;
-               }
-
-               if (nextvar) {
-                       /* We have a variable.  Find the start and end, and determine
-                          if we are going to have to recursively call ourselves on the
-                          contents */
-                       vars = vare = nextvar + 2;
-                       brackets = 1;
-                       needsub = 0;
-
-                       /* Find the end of it */
-                       while (brackets && *vare) {
-                               if ((vare[0] == '$') && (vare[1] == '{')) {
-                                       needsub++;
-                               } else if (vare[0] == '{') {
-                                       brackets++;
-                               } else if (vare[0] == '}') {
-                                       brackets--;
-                               } else if ((vare[0] == '$') && (vare[1] == '['))
-                                       needsub++;
-                               vare++;
-                       }
-                       if (brackets)
-                               ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
-                       len = vare - vars - 1;
-
-                       /* Skip totally over variable string */
-                       whereweare += (len + 3);
-
-                       if (!var)
-                               var = ast_alloca(VAR_BUF_SIZE);
-
-                       /* Store variable name (and truncate) */
-                       ast_copy_string(var, vars, len + 1);
-
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
-
-                               if (!ltmp) {
-                                       ltmp = ast_alloca(VAR_BUF_SIZE);
-                               }
-                               pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
-                               vars = ltmp;
-                       } else {
-                               vars = var;
-                       }
-
-                       if (!workspace)
-                               workspace = ast_alloca(VAR_BUF_SIZE);
-
-                       workspace[0] = '\0';
-
-                       parse_variable_name(vars, &offset, &offset2, &isfunction);
-                       if (isfunction) {
-                               /* Evaluate function */
-                               if (c || !headp)
-                                       cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
-                               else {
-                                       struct varshead old;
-                                       struct ast_channel *c = ast_dummy_channel_alloc();
-                                       if (c) {
-                                               memcpy(&old, ast_channel_varshead(c), sizeof(old));
-                                               memcpy(ast_channel_varshead(c), headp, sizeof(*ast_channel_varshead(c)));
-                                               cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
-                                               /* Don't deallocate the varshead that was passed in */
-                                               memcpy(ast_channel_varshead(c), &old, sizeof(*ast_channel_varshead(c)));
-                                               c = ast_channel_unref(c);
-                                       } else {
-                                               ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
-                                       }
-                               }
-                               ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");
-                       } else {
-                               /* Retrieve variable value */
-                               pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
-                       }
-                       if (cp4) {
-                               cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
-
-                               length = strlen(cp4);
-                               if (length > count)
-                                       length = count;
-                               memcpy(cp2, cp4, length);
-                               count -= length;
-                               cp2 += length;
-                               *cp2 = 0;
-                       }
-               } else if (nextexp) {
-                       /* We have an expression.  Find the start and end, and determine
-                          if we are going to have to recursively call ourselves on the
-                          contents */
-                       vars = vare = nextexp + 2;
-                       brackets = 1;
-                       needsub = 0;
-
-                       /* Find the end of it */
-                       while (brackets && *vare) {
-                               if ((vare[0] == '$') && (vare[1] == '[')) {
-                                       needsub++;
-                                       brackets++;
-                                       vare++;
-                               } else if (vare[0] == '[') {
-                                       brackets++;
-                               } else if (vare[0] == ']') {
-                                       brackets--;
-                               } else if ((vare[0] == '$') && (vare[1] == '{')) {
-                                       needsub++;
-                                       vare++;
-                               }
-                               vare++;
-                       }
-                       if (brackets)
-                               ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
-                       len = vare - vars - 1;
-
-                       /* Skip totally over expression */
-                       whereweare += (len + 3);
-
-                       if (!var)
-                               var = ast_alloca(VAR_BUF_SIZE);
-
-                       /* Store variable name (and truncate) */
-                       ast_copy_string(var, vars, len + 1);
-
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
-
-                               if (!ltmp) {
-                                       ltmp = ast_alloca(VAR_BUF_SIZE);
-                               }
-                               pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
-                               vars = ltmp;
-                       } else {
-                               vars = var;
-                       }
+       struct ast_exten *e;
 
-                       length = ast_expr(vars, cp2, count, c);
+       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
+               return -1;                   /* No hint, return -1 */
+       }
 
-                       if (length) {
-                               ast_debug(1, "Expression result is '%s'\n", cp2);
-                               count -= length;
-                               cp2 += length;
-                               *cp2 = 0;
-                       }
+       if (e->exten[0] == '_') {
+               /* Create this hint on-the-fly */
+               ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
+                       e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
+                       e->registrar);
+               if (!(e = ast_hint_extension(c, context, exten))) {
+                       /* Improbable, but not impossible */
+                       return -1;
                }
        }
-       *used = cp2 - orig_cp2;
-}
 
-void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
-{
-       size_t used;
-       pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, &used);
+       return extension_presence_state_helper(e, subtype, message);
 }
 
-void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+static int execute_state_callback(ast_state_cb_type cb,
+       const char *context,
+       const char *exten,
+       void *data,
+       enum ast_state_cb_update_reason reason,
+       struct ast_hint *hint,
+       struct ao2_container *device_state_info)
 {
-       size_t used;
-       pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, &used);
-}
+       int res = 0;
+       struct ast_state_cb_info info = { 0, };
 
-static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
-{
-       const char *tmp;
+       info.reason = reason;
 
-       /* Nothing more to do */
-       if (!e->data) {
-               *passdata = '\0';
-               return;
+       /* Copy over current hint data */
+       if (hint) {
+               ao2_lock(hint);
+               info.exten_state = hint->laststate;
+               info.device_state_info = device_state_info;
+               info.presence_state = hint->last_presence_state;
+               if (!(ast_strlen_zero(hint->last_presence_subtype))) {
+                       info.presence_subtype = ast_strdupa(hint->last_presence_subtype);
+               } else {
+                       info.presence_subtype = "";
+               }
+               if (!(ast_strlen_zero(hint->last_presence_message))) {
+                       info.presence_message = ast_strdupa(hint->last_presence_message);
+               } else {
+                       info.presence_message = "";
+               }
+               ao2_unlock(hint);
+       } else {
+               info.exten_state = AST_EXTENSION_REMOVED;
        }
 
-       /* No variables or expressions in e->data, so why scan it? */
-       if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
-               ast_copy_string(passdata, e->data, datalen);
-               return;
-       }
+       res = cb(context, exten, &info, data);
 
-       pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
+       return res;
 }
 
 /*!
- * \brief The return value depends on the action:
- *
- * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
- *     and return 0 on failure, -1 on match;
- * E_FINDLABEL maps the label to a priority, and returns
- *     the priority on success, ... XXX
- * E_SPAWN, spawn an application,
- *
- * \retval 0 on success.
- * \retval  -1 on failure.
+ * /internal
+ * /brief Identify a channel for every device which is supposedly responsible for the device state.
  *
- * \note The channel is auto-serviced in this function, because doing an extension
- * match may block for a long time.  For example, if the lookup has to use a network
- * dialplan switch, such as DUNDi or IAX2, it may take a while.  However, the channel
- * auto-service code will queue up any important signalling frames to be processed
- * after this is done.
+ * Especially when the device is ringing, the oldest ringing channel is chosen.
+ * For all other cases the first encountered channel in the specific state is chosen.
  */
-static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
-  const char *context, const char *exten, int priority,
-  const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
+static void get_device_state_causing_channels(struct ao2_container *c)
 {
-       struct ast_exten *e;
-       struct ast_app *app;
-       int res;
-       struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
-       char passdata[EXT_DATA_SIZE];
-       int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
-       RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+       struct ao2_iterator iter;
+       struct ast_device_state_info *info;
+       struct ast_channel *chan;
 
-       ast_rdlock_contexts();
-       if (found)
-               *found = 0;
+       if (!c || !ao2_container_count(c)) {
+               return;
+       }
+       iter = ao2_iterator_init(c, 0);
+       for (; (info = ao2_iterator_next(&iter)); ao2_ref(info, -1)) {
+               enum ast_channel_state search_state = 0; /* prevent false uninit warning */
+               char match[AST_CHANNEL_NAME];
+               struct ast_channel_iterator *chan_iter;
+               struct timeval chantime = {0, }; /* prevent false uninit warning */
 
-       e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
-       if (e) {
-               if (found)
-                       *found = 1;
-               if (matching_action) {
-                       ast_unlock_contexts();
-                       return -1;      /* success, we found it */
-               } else if (action == E_FINDLABEL) { /* map the label to a priority */
-                       res = e->priority;
-                       ast_unlock_contexts();
-                       return res;     /* the priority we were looking for */
-               } else {        /* spawn */
-                       if (!e->cached_app)
-                               e->cached_app = pbx_findapp(e->app);
-                       app = e->cached_app;
-                       ast_unlock_contexts();
-                       if (!app) {
-                               ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
-                               return -1;
-                       }
-                       if (ast_channel_context(c) != context)
-                               ast_channel_context_set(c, context);
-                       if (ast_channel_exten(c) != exten)
-                               ast_channel_exten_set(c, exten);
-                       ast_channel_priority_set(c, priority);
-                       pbx_substitute_variables(passdata, sizeof(passdata), c, e);
-                       ast_debug(1, "Launching '%s'\n", app->name);
-                       {
-                               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_BRMAGENTA, 0, ast_channel_name(c)),
-                                       COLORIZE(COLOR_BRMAGENTA, 0, passdata),
-                                       "in new stack");
-                       }
-                       return pbx_exec(c, app, passdata);      /* 0 on success, -1 on failure */
-               }
-       } else if (q.swo) {     /* not found here, but in another switch */
-               if (found)
-                       *found = 1;
-               ast_unlock_contexts();
-               if (matching_action) {
-                       return -1;
-               } else {
-                       if (!q.swo->exec) {
-                               ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
-                               res = -1;
-                       }
-                       return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
-               }
-       } else {        /* not found anywhere, see what happened */
-               ast_unlock_contexts();
-               /* Using S_OR here because Solaris doesn't like NULL being passed to ast_log */
-               switch (q.status) {
-               case STATUS_NO_CONTEXT:
-                       if (!matching_action && !combined_find_spawn)
-                               ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", S_OR(context, ""));
-                       break;
-               case STATUS_NO_EXTENSION:
-                       if (!matching_action && !combined_find_spawn)
-                               ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, S_OR(context, ""));
+               switch (info->device_state) {
+               case AST_DEVICE_RINGING:
+               case AST_DEVICE_RINGINUSE:
+                       /* find ringing channel */
+                       search_state = AST_STATE_RINGING;
                        break;
-               case STATUS_NO_PRIORITY:
-                       if (!matching_action && !combined_find_spawn)
-                               ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, S_OR(context, ""));
+               case AST_DEVICE_BUSY:
+                       /* find busy channel */
+                       search_state = AST_STATE_BUSY;
                        break;
-               case STATUS_NO_LABEL:
-                       if (context && !combined_find_spawn)
-                               ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, S_OR(context, ""));
+               case AST_DEVICE_ONHOLD:
+               case AST_DEVICE_INUSE:
+                       /* find up channel */
+                       search_state = AST_STATE_UP;
                        break;
-               default:
-                       ast_debug(1, "Shouldn't happen!\n");
+               case AST_DEVICE_UNKNOWN:
+               case AST_DEVICE_NOT_INUSE:
+               case AST_DEVICE_INVALID:
+               case AST_DEVICE_UNAVAILABLE:
+               case AST_DEVICE_TOTAL /* not a state */:
+                       /* no channels are of interest */
+                       continue;
                }
 
-               return (matching_action) ? 0 : -1;
+               /* iterate over all channels of the device */
+               snprintf(match, sizeof(match), "%s-", info->device_name);
+               chan_iter = ast_channel_iterator_by_name_new(match, strlen(match));
+               for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {
+                       ast_channel_lock(chan);
+                       /* this channel's state doesn't match */
+                       if (search_state != ast_channel_state(chan)) {
+                               ast_channel_unlock(chan);
+                               continue;
+                       }
+                       /* any non-ringing channel will fit */
+                       if (search_state != AST_STATE_RINGING) {
+                               ast_channel_unlock(chan);
+                               info->causing_channel = chan; /* is kept ref'd! */
+                               break;
+                       }
+                       /* but we need the oldest ringing channel of the device to match with undirected pickup */
+                       if (!info->causing_channel) {
+                               chantime = ast_channel_creationtime(chan);
+                               ast_channel_ref(chan); /* must ref it! */
+                               info->causing_channel = chan;
+                       } else if (ast_tvcmp(ast_channel_creationtime(chan), chantime) < 0) {
+                               chantime = ast_channel_creationtime(chan);
+                               ast_channel_unref(info->causing_channel);
+                               ast_channel_ref(chan); /* must ref it! */
+                               info->causing_channel = chan;
+                       }
+                       ast_channel_unlock(chan);
+               }
+               ast_channel_iterator_destroy(chan_iter);
        }
+       ao2_iterator_destroy(&iter);
 }
 
-/*! \brief Find hint for given extension in context */
-static struct ast_exten *ast_hint_extension_nolock(struct ast_channel *c, const char *context, const char *exten)
-{
-       struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
-       return pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
-}
-
-static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
 {
-       struct ast_exten *e;
-       ast_rdlock_contexts();
-       e = ast_hint_extension_nolock(c, context, exten);
-       ast_unlock_contexts();
-       return e;
-}
+       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];
 
-enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
-{
-       switch (devstate) {
-       case AST_DEVICE_ONHOLD:
-               return AST_EXTENSION_ONHOLD;
-       case AST_DEVICE_BUSY:
-               return AST_EXTENSION_BUSY;
-       case AST_DEVICE_UNKNOWN:
-               return AST_EXTENSION_NOT_INUSE;
-       case AST_DEVICE_UNAVAILABLE:
-       case AST_DEVICE_INVALID:
-               return AST_EXTENSION_UNAVAILABLE;
-       case AST_DEVICE_RINGINUSE:
-               return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
-       case AST_DEVICE_RINGING:
-               return AST_EXTENSION_RINGING;
-       case AST_DEVICE_INUSE:
-               return AST_EXTENSION_INUSE;
-       case AST_DEVICE_NOT_INUSE:
-               return AST_EXTENSION_NOT_INUSE;
-       case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
-               break;
+       ao2_lock(hint);
+       if (!hint->exten) {
+               /* The extension has already been destroyed */
+               ao2_unlock(hint);
+               return;
        }
 
-       return AST_EXTENSION_NOT_INUSE;
-}
+       /*
+        * 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);
 
-/*!
- * \internal
- * \brief Parse out the presence portion of the hint string
- */
-static char *parse_hint_presence(struct ast_str *hint_args)
-{
-       char *copy = ast_strdupa(ast_str_buffer(hint_args));
-       char *tmp = "";
+       /*
+        * 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)
+        */
 
-       if ((tmp = strrchr(copy, ','))) {
-               *tmp = '\0';
-               tmp++;
-       } else {
-               return NULL;
+       /* 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;
        }
-       ast_str_set(&hint_args, 0, "%s", tmp);
-       return ast_str_buffer(hint_args);
-}
 
-/*!
- * \internal
- * \brief Parse out the device portion of the hint string
- */
-static char *parse_hint_device(struct ast_str *hint_args)
-{
-       char *copy = ast_strdupa(ast_str_buffer(hint_args));
-       char *tmp;
+       /* Device state changed since last check - notify the watchers. */
+       hint->laststate = state;        /* record we saw the change */
 
-       if ((tmp = strrchr(copy, ','))) {
-               *tmp = '\0';
+       /* 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);
        }
 
-       ast_str_set(&hint_args, 0, "%s", copy);
-       return ast_str_buffer(hint_args);
+       /* 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 device_state_info_dt(void *obj)
+static void presence_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app,
+                                           struct ast_presence_state_message *presence_state)
 {
-       struct ast_device_state_info *info = obj;
+       struct ao2_iterator cb_iter;
+       struct ast_state_cb *state_cb;
+       char context_name[AST_MAX_CONTEXT];
+       char exten_name[AST_MAX_EXTENSION];
 
-       ao2_cleanup(info->causing_channel);
-}
+       ao2_lock(hint);
+       if (!hint->exten) {
+               /* The extension has already been destroyed */
+               ao2_unlock(hint);
+               return;
+       }
 
-static struct ao2_container *alloc_device_state_info(void)
-{
-       return ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
-}
+       /*
+        * 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);
 
-static int ast_extension_state3(struct ast_str *hint_app, struct ao2_container *device_state_info)
+       /* 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)
 {
-       char *cur;
-       char *rest;
-       struct ast_devstate_aggregate agg;
+       struct ast_hint *hint;
+       struct ast_str *hint_app;
 
-       /* One or more devices separated with a & character */
-       rest = parse_hint_device(hint_app);
+       if (hint_change_message_type() != stasis_message_type(msg)) {
+               return 0;
+       }
 
-       ast_devstate_aggregate_init(&agg);
-       while ((cur = strsep(&rest, "&"))) {
-               enum ast_device_state state = ast_device_state(cur);
+       if (!(hint_app = ast_str_create(1024))) {
+               return -1;
+       }
 
-               ast_devstate_aggregate_add(&agg, state);
-               if (device_state_info) {
-                       struct ast_device_state_info *obj;
+       hint = stasis_message_data(msg);
 
-                       obj = ao2_alloc_options(sizeof(*obj) + strlen(cur), device_state_info_dt, AO2_ALLOC_OPT_LOCK_NOLOCK);
-                       /* if failed we cannot add this device */
-                       if (obj) {
-                               obj->device_state = state;
-                               strcpy(obj->device_name, cur);
-                               ao2_link(device_state_info, obj);
-                               ao2_ref(obj, -1);
+       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;
        }
 
-       return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
+       ast_free(hint_app);
+       return 1;
 }
 
-/*! \brief Check state of extension by using hints */
-static int ast_extension_state2(struct ast_exten *e, struct ao2_container *device_state_info)
+static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
-       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+       struct ast_device_state_message *dev_state;
+       struct ast_str *hint_app;
+       struct ast_hintdevice *device;
+       struct ast_hintdevice *cmpdevice;
+       struct ao2_iterator *dev_iter;
+       struct ao2_iterator auto_iter;
+       struct ast_autohint *autohint;
+       char *virtual_device;
+       char *type;
+       char *device_name;
 
-       if (!e || !hint_app) {
-               return -1;
+       if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
+               return;
        }
 
-       ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
-       return ast_extension_state3(hint_app, device_state_info);
-}
-
-/*! \brief Return extension_state as string */
-const char *ast_extension_state2str(int extension_state)
-{
-       int i;
-
-       for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
-               if (extension_states[i].extension_state == extension_state)
-                       return extension_states[i].text;
+       if (ast_device_state_message_type() != stasis_message_type(msg)) {
+               return;
        }
-       return "Unknown";
-}
 
-/*!
- * \internal
- * \brief Check extension state for an extension by using hint
- */
-static int internal_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
-       struct ao2_container *device_state_info)
-{
-       struct ast_exten *e;
+       dev_state = stasis_message_data(msg);
+       if (dev_state->eid) {
+               /* ignore non-aggregate states */
+               return;
+       }
 
-       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
-               return -1;                   /* No hint, return -1 */
+       if (ao2_container_count(hintdevices) == 0 && ao2_container_count(autohints) == 0) {
+               /* There are no hints monitoring devices. */
+               return;
        }
 
-       if (e->exten[0] == '_') {
-               /* Create this hint on-the-fly */
-               ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
-                       e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
-                       e->registrar);
-               if (!(e = ast_hint_extension(c, context, exten))) {
-                       /* Improbable, but not impossible */
-                       return -1;
-               }
+       hint_app = ast_str_create(1024);
+       if (!hint_app) {
+               return;
        }
 
-       return ast_extension_state2(e, device_state_info);  /* Check all devices in the hint */
-}
+       cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(dev_state->device));
+       strcpy(cmpdevice->hintdevice, dev_state->device);
 
-/*! \brief Check extension state for an extension by using hint */
-int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
-{
-       return internal_extension_state_extended(c, context, exten, NULL);
-}
+       ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
 
-/*! \brief Check extended extension state for an extension by using hint */
-int ast_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
-       struct ao2_container **device_state_info)
-{
-       struct ao2_container *container = NULL;
-       int ret;
+       /* 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) {
+               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);
+       }
 
-       if (device_state_info) {
-               container = alloc_device_state_info();
+       /* 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;
        }
 
-       ret = internal_extension_state_extended(c, context, exten, container);
-       if (ret < 0 && container) {
-               ao2_ref(container, -1);
-               container = NULL;
+       /* 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_state_info) {
-               get_device_state_causing_channels(container);
-               *device_state_info = container;
+       /* Invalid device state name - not a virtual/custom device and not a real device */
+       if (ast_strlen_zero(device_name)) {
+               goto end;
        }
 
-       return ret;
-}
+       *device_name++ = '\0';
 
-static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
-{
-       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
-       char *presence_provider;
-       const char *app;
+       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;
+               }
 
-       if (!e || !hint_app) {
-               return -1;
-       }
+               /* 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);
 
-       app = ast_get_extension_app(e);
-       if (ast_strlen_zero(app)) {
-               return -1;
+               /* Since this hint was just created there are no watchers, so we don't need to notify anyone */
        }
+       ao2_iterator_destroy(&auto_iter);
 
-       ast_str_set(&hint_app, 0, "%s", app);
-       presence_provider = parse_hint_presence(hint_app);
+end:
+       ast_mutex_unlock(&context_merge_lock);
+       ast_free(hint_app);
+       return;
+}
 
-       if (ast_strlen_zero(presence_provider)) {
-               /* No presence string in the hint */
-               return 0;
-       }
+/*!
+ * \internal
+ * \brief Destroy the given state callback object.
+ *
+ * \param doomed State callback to destroy.
+ *
+ * \return Nothing
+ */
+static void destroy_state_cb(void *doomed)
+{
+       struct ast_state_cb *state_cb = doomed;
 
-       return ast_presence_state(presence_provider, subtype, message);
+       if (state_cb->destroy_cb) {
+               state_cb->destroy_cb(state_cb->id, state_cb->data);
+       }
 }
 
-int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
+/*!
+ * \internal
+ * \brief Add watcher for extension states with destructor
+ */
+static int 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, int extended)
 {
+       struct ast_hint *hint;
+       struct ast_state_cb *state_cb;
        struct ast_exten *e;
+       int id;
 
-       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
-               return -1;                   /* No hint, return -1 */
+       /* If there's no context and extension:  add callback to statecbs list */
+       if (!context && !exten) {
+               /* Prevent multiple adds from adding the same change_cb at the same time. */
+               ao2_lock(statecbs);
+
+               /* Remove any existing change_cb. */
+               ao2_find(statecbs, change_cb, OBJ_UNLINK | OBJ_NODATA);
+
+               /* Now insert the change_cb */
+               if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
+                       ao2_unlock(statecbs);
+                       return -1;
+               }
+               state_cb->id = 0;
+               state_cb->change_cb = change_cb;
+               state_cb->destroy_cb = destroy_cb;
+               state_cb->data = data;
+               state_cb->extended = extended;
+               ao2_link(statecbs, state_cb);
+
+               ao2_ref(state_cb, -1);
+               ao2_unlock(statecbs);
+               return 0;
+       }
+
+       if (!context || !exten)
+               return -1;
+
+       /* This callback type is for only one hint, so get the hint */
+       e = ast_hint_extension(NULL, context, exten);
+       if (!e) {
+               return -1;
        }
 
+       /* If this is a pattern, dynamically create a new extension for this
+        * particular match.  Note that this will only happen once for each
+        * individual extension, because the pattern will no longer match first.
+        */
        if (e->exten[0] == '_') {
-               /* Create this hint on-the-fly */
                ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
                        e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
                        e->registrar);
-               if (!(e = ast_hint_extension(c, context, exten))) {
-                       /* Improbable, but not impossible */
+               e = ast_hint_extension(NULL, context, exten);
+               if (!e || e->exten[0] == '_') {
                        return -1;
                }
        }
 
-       return extension_presence_state_helper(e, subtype, message);
+       /* Find the hint in the hints container */
+       ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
+       hint = ao2_find(hints, e, 0);
+       if (!hint) {
+               ao2_unlock(hints);
+               return -1;
+       }
+
+       /* Now insert the callback in the callback list  */
+       if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
+               ao2_ref(hint, -1);
+               ao2_unlock(hints);
+               return -1;
+       }
+       do {
+               id = stateid++;         /* Unique ID for this callback */
+               /* Do not allow id to ever be -1 or 0. */
+       } while (id == -1 || id == 0);
+       state_cb->id = id;
+       state_cb->change_cb = change_cb;        /* Pointer to callback routine */
+       state_cb->destroy_cb = destroy_cb;
+       state_cb->data = data;          /* Data for the callback */
+       state_cb->extended = extended;
+       ao2_link(hint->callbacks, state_cb);
+
+       ao2_ref(state_cb, -1);
+       ao2_ref(hint, -1);
+       ao2_unlock(hints);
+
+       return id;
 }
 
-static int execute_state_callback(ast_state_cb_type cb,
-       const char *context,
-       const char *exten,
-       void *data,
-       enum ast_state_cb_update_reason reason,
-       struct ast_hint *hint,
-       struct ao2_container *device_state_info)
+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)
 {
-       int res = 0;
-       struct ast_state_cb_info info = { 0, };
-
-       info.reason = reason;
+       return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 0);
+}
 
-       /* Copy over current hint data */
-       if (hint) {
-               ao2_lock(hint);
-               info.exten_state = hint->laststate;
-               info.device_state_info = device_state_info;
-               info.presence_state = hint->last_presence_state;
-               if (!(ast_strlen_zero(hint->last_presence_subtype))) {
-                       info.presence_subtype = ast_strdupa(hint->last_presence_subtype);
-               } else {
-                       info.presence_subtype = "";
-               }
-               if (!(ast_strlen_zero(hint->last_presence_message))) {
-                       info.presence_message = ast_strdupa(hint->last_presence_message);
-               } else {
-                       info.presence_message = "";
-               }
-               ao2_unlock(hint);
-       } else {
-               info.exten_state = AST_EXTENSION_REMOVED;
-       }
+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);
+}
 
-       /* NOTE: The casts will not be needed for v10 and later */
-       res = cb((char *) context, (char *) exten, &info, data);
+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);
+}
 
-       return res;
+int ast_extension_state_add_extended(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, 1);
 }
 
-/*!
- * /internal
- * /brief Identify a channel for every device which is supposedly responsible for the device state.
- *
- * Especially when the device is ringing, the oldest ringing channel is chosen.
- * For all other cases the first encountered channel in the specific state is chosen.
- */
-static void get_device_state_causing_channels(struct ao2_container *c)
+/*! \brief Find Hint by callback id */
+static int find_hint_by_cb_id(void *obj, void *arg, int flags)
 {
-       struct ao2_iterator iter;
-       struct ast_device_state_info *info;
-       struct ast_channel *chan;
+       struct ast_state_cb *state_cb;
+       const struct ast_hint *hint = obj;
+       int *id = arg;
 
-       if (!c || !ao2_container_count(c)) {
-               return;
+       if ((state_cb = ao2_find(hint->callbacks, id, 0))) {
+               ao2_ref(state_cb, -1);
+               return CMP_MATCH | CMP_STOP;
        }
-       iter = ao2_iterator_init(c, 0);
-       for (; (info = ao2_iterator_next(&iter)); ao2_ref(info, -1)) {
-               enum ast_channel_state search_state = 0; /* prevent false uninit warning */
-               char match[AST_CHANNEL_NAME];
-               struct ast_channel_iterator *chan_iter;
-               struct timeval chantime = {0, }; /* prevent false uninit warning */
-
-               switch (info->device_state) {
-               case AST_DEVICE_RINGING:
-               case AST_DEVICE_RINGINUSE:
-                       /* find ringing channel */
-                       search_state = AST_STATE_RINGING;
-                       break;
-               case AST_DEVICE_BUSY:
-                       /* find busy channel */
-                       search_state = AST_STATE_BUSY;
-                       break;
-               case AST_DEVICE_ONHOLD:
-               case AST_DEVICE_INUSE:
-                       /* find up channel */
-                       search_state = AST_STATE_UP;
-                       break;
-               case AST_DEVICE_UNKNOWN:
-               case AST_DEVICE_NOT_INUSE:
-               case AST_DEVICE_INVALID:
-               case AST_DEVICE_UNAVAILABLE:
-               case AST_DEVICE_TOTAL /* not a state */:
-                       /* no channels are of interest */
-                       continue;
-               }
 
-               /* iterate over all channels of the device */
-               snprintf(match, sizeof(match), "%s-", info->device_name);
-               chan_iter = ast_channel_iterator_by_name_new(match, strlen(match));
-               for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {
-                       ast_channel_lock(chan);
-                       /* this channel's state doesn't match */
-                       if (search_state != ast_channel_state(chan)) {
-                               ast_channel_unlock(chan);
-                               continue;
-                       }
-                       /* any non-ringing channel will fit */
-                       if (search_state != AST_STATE_RINGING) {
-                               ast_channel_unlock(chan);
-                               info->causing_channel = chan; /* is kept ref'd! */
-                               break;
-                       }
-                       /* but we need the oldest ringing channel of the device to match with undirected pickup */
-                       if (!info->causing_channel) {
-                               chantime = ast_channel_creationtime(chan);
-                               ast_channel_ref(chan); /* must ref it! */
-                               info->causing_channel = chan;
-                       } else if (ast_tvcmp(ast_channel_creationtime(chan), chantime) < 0) {
-                               chantime = ast_channel_creationtime(chan);
-                               ast_channel_unref(info->causing_channel);
-                               ast_channel_ref(chan); /* must ref it! */
-                               info->causing_channel = chan;
-                       }
-                       ast_channel_unlock(chan);
-               }
-               ast_channel_iterator_destroy(chan_iter);
-       }
-       ao2_iterator_destroy(&iter);
+       return 0;
 }
 
-static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
+int ast_extension_state_del(int id, ast_state_cb_type change_cb)
 {
-       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 ast_state_cb *p_cur;
+       int ret = -1;
 
-       if (ast_device_state_message_type() != stasis_message_type(msg)) {
-               return;
-       }
+       if (!id) {      /* id == 0 is a callback without extension */
+               if (!change_cb) {
+                       return ret;
+               }
+               p_cur = ao2_find(statecbs, change_cb, OBJ_UNLINK);
+               if (p_cur) {
+                       ret = 0;
+                       ao2_ref(p_cur, -1);
+               }
+       } else { /* callback with extension, find the callback based on ID */
+               struct ast_hint *hint;
 
-       dev_state = stasis_message_data(msg);
-       if (dev_state->eid) {
-               /* ignore non-aggregate states */
-               return;
+               ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
+               hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id);
+               if (hint) {
+                       p_cur = ao2_find(hint->callbacks, &id, OBJ_UNLINK);
+                       if (p_cur) {
+                               ret = 0;
+                               ao2_ref(p_cur, -1);
+                       }
+                       ao2_ref(hint, -1);
+               }
+               ao2_unlock(hints);
        }
 
-       if (ao2_container_count(hintdevices) == 0) {
-               /* There are no hints monitoring devices. */
-               return;
-       }
+       return ret;
+}
 
-       hint_app = ast_str_create(1024);
-       if (!hint_app) {
-               return;
-       }
+static int hint_id_cmp(void *obj, void *arg, int flags)
+{
+       const struct ast_state_cb *cb = obj;
+       int *id = arg;
 
-       cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(dev_state->device));
-       strcpy(cmpdevice->hintdevice, dev_state->device);
+       return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
+}
 
-       ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
-       dev_iter = ao2_t_callback(hintdevices,
-               OBJ_POINTER | OBJ_MULTIPLE,
-               hintdevice_cmp_multiple,
-               cmpdevice,
-               "find devices in container");
-       if (!dev_iter) {
-               ast_mutex_unlock(&context_merge_lock);
-               ast_free(hint_app);
-               return;
-       }
+/*!
+ * \internal
+ * \brief Destroy the given hint object.
+ *
+ * \param obj Hint to destroy.
+ *
+ * \return Nothing
+ */
+static void destroy_hint(void *obj)
+{
+       struct ast_hint *hint = obj;
+       int i;
 
-       for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
+       if (hint->callbacks) {
                struct ast_state_cb *state_cb;
-               int state;
-               int same_state;
-               struct ao2_container *device_state_info;
-               int first_extended_cb_call = 1;
-
-               if (!device->hint) {
-                       /* Should never happen. */
-                       continue;
-               }
-               hint = device->hint;
+               const char *context_name;
+               const char *exten_name;
 
-               ao2_lock(hint);
-               if (!hint->exten) {
+               if (hint->exten) {
+                       context_name = ast_get_context_name(ast_get_extension_context(hint->exten));
+                       exten_name = ast_get_extension_name(hint->exten);
+                       hint->exten = NULL;
+               } else {
                        /* 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;
+                       context_name = hint->context_name;
+                       exten_name = hint->exten_name;
                }
-
-               /* Device state changed since last check - notify the watchers. */
-               hint->laststate = state;        /* record we saw the change */
-
-               /* 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)) {
+               hint->laststate = AST_EXTENSION_DEACTIVATED;
+               while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
+                       /* Notify with -1 and remove all callbacks */
                        execute_state_callback(state_cb->change_cb,
                                context_name,
                                exten_name,
@@ -5242,278 +3877,19 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
                                AST_HINT_UPDATE_DEVICE,
                                hint,
                                NULL);
+                       ao2_ref(state_cb, -1);
                }
-               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_ref(hint->callbacks, -1);
+       }
 
-               ao2_cleanup(device_state_info);
+       for (i = 0; i < AST_VECTOR_SIZE(&hint->devices); i++) {
+               char *device = AST_VECTOR_GET(&hint->devices, i);
+               ast_free(device);
        }
-       ast_mutex_unlock(&context_merge_lock);
-
-       ao2_iterator_destroy(dev_iter);
-       ast_free(hint_app);
-       return;
-}
-
-/*!
- * \internal
- * \brief Destroy the given state callback object.
- *
- * \param doomed State callback to destroy.
- *
- * \return Nothing
- */
-static void destroy_state_cb(void *doomed)
-{
-       struct ast_state_cb *state_cb = doomed;
-
-       if (state_cb->destroy_cb) {
-               state_cb->destroy_cb(state_cb->id, state_cb->data);
-       }
-}
-
-/*!
- * \internal
- * \brief Add watcher for extension states with destructor
- */
-static int 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, int extended)
-{
-       struct ast_hint *hint;
-       struct ast_state_cb *state_cb;
-       struct ast_exten *e;
-       int id;
-
-       /* If there's no context and extension:  add callback to statecbs list */
-       if (!context && !exten) {
-               /* Prevent multiple adds from adding the same change_cb at the same time. */
-               ao2_lock(statecbs);
-
-               /* Remove any existing change_cb. */
-               ao2_find(statecbs, change_cb, OBJ_UNLINK | OBJ_NODATA);
-
-               /* Now insert the change_cb */
-               if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
-                       ao2_unlock(statecbs);
-                       return -1;
-               }
-               state_cb->id = 0;
-               state_cb->change_cb = change_cb;
-               state_cb->destroy_cb = destroy_cb;
-               state_cb->data = data;
-               state_cb->extended = extended;
-               ao2_link(statecbs, state_cb);
-
-               ao2_ref(state_cb, -1);
-               ao2_unlock(statecbs);
-               return 0;
-       }
-
-       if (!context || !exten)
-               return -1;
-
-       /* This callback type is for only one hint, so get the hint */
-       e = ast_hint_extension(NULL, context, exten);
-       if (!e) {
-               return -1;
-       }
-
-       /* If this is a pattern, dynamically create a new extension for this
-        * particular match.  Note that this will only happen once for each
-        * individual extension, because the pattern will no longer match first.
-        */
-       if (e->exten[0] == '_') {
-               ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
-                       e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
-                       e->registrar);
-               e = ast_hint_extension(NULL, context, exten);
-               if (!e || e->exten[0] == '_') {
-                       return -1;
-               }
-       }
-
-       /* Find the hint in the hints container */
-       ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
-       hint = ao2_find(hints, e, 0);
-       if (!hint) {
-               ao2_unlock(hints);
-               return -1;
-       }
-
-       /* Now insert the callback in the callback list  */
-       if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
-               ao2_ref(hint, -1);
-               ao2_unlock(hints);
-               return -1;
-       }
-       do {
-               id = stateid++;         /* Unique ID for this callback */
-               /* Do not allow id to ever be -1 or 0. */
-       } while (id == -1 || id == 0);
-       state_cb->id = id;
-       state_cb->change_cb = change_cb;        /* Pointer to callback routine */
-       state_cb->destroy_cb = destroy_cb;
-       state_cb->data = data;          /* Data for the callback */
-       state_cb->extended = extended;
-       ao2_link(hint->callbacks, state_cb);
-
-       ao2_ref(state_cb, -1);
-       ao2_ref(hint, -1);
-       ao2_unlock(hints);
-
-       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)
-{
-       return extension_state_add_destroy(context, exten, change_cb, NULL, data, 1);
-}
-
-/*! \brief Find Hint by callback id */
-static int find_hint_by_cb_id(void *obj, void *arg, int flags)
-{
-       struct ast_state_cb *state_cb;
-       const struct ast_hint *hint = obj;
-       int *id = arg;
-
-       if ((state_cb = ao2_find(hint->callbacks, id, 0))) {
-               ao2_ref(state_cb, -1);
-               return CMP_MATCH | CMP_STOP;
-       }
-
-       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;
-       int ret = -1;
-
-       if (!id) {      /* id == 0 is a callback without extension */
-               if (!change_cb) {
-                       return ret;
-               }
-               p_cur = ao2_find(statecbs, change_cb, OBJ_UNLINK);
-               if (p_cur) {
-                       ret = 0;
-                       ao2_ref(p_cur, -1);
-               }
-       } else { /* callback with extension, find the callback based on ID */
-               struct ast_hint *hint;
-
-               ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
-               hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id);
-               if (hint) {
-                       p_cur = ao2_find(hint->callbacks, &id, OBJ_UNLINK);
-                       if (p_cur) {
-                               ret = 0;
-                               ao2_ref(p_cur, -1);
-                       }
-                       ao2_ref(hint, -1);
-               }
-               ao2_unlock(hints);
-       }
-
-       return ret;
-}
-
-static int hint_id_cmp(void *obj, void *arg, int flags)
-{
-       const struct ast_state_cb *cb = obj;
-       int *id = arg;
-
-       return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
-}
-
-/*!
- * \internal
- * \brief Destroy the given hint object.
- *
- * \param obj Hint to destroy.
- *
- * \return Nothing
- */
-static void destroy_hint(void *obj)
-{
-       struct ast_hint *hint = obj;
-
-       if (hint->callbacks) {
-               struct ast_state_cb *state_cb;
-               const char *context_name;
-               const char *exten_name;
-
-               if (hint->exten) {
-                       context_name = ast_get_context_name(ast_get_extension_context(hint->exten));
-                       exten_name = ast_get_extension_name(hint->exten);
-                       hint->exten = NULL;
-               } else {
-                       /* The extension has already been destroyed */
-                       context_name = hint->context_name;
-                       exten_name = hint->exten_name;
-               }
-               hint->laststate = AST_EXTENSION_DEACTIVATED;
-               while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
-                       /* Notify with -1 and remove all callbacks */
-                       execute_state_callback(state_cb->change_cb,
-                               context_name,
-                               exten_name,
-                               state_cb->data,
-                               AST_HINT_UPDATE_DEVICE,
-                               hint,
-                               NULL);
-                       ao2_ref(state_cb, -1);
-               }
-               ao2_ref(hint->callbacks, -1);
-       }
-       ast_free(hint->last_presence_subtype);
-       ast_free(hint->last_presence_message);
-}
+       AST_VECTOR_FREE(&hint->devices);
+       ast_free(hint->last_presence_subtype);
+       ast_free(hint->last_presence_message);
+}
 
 /*! \brief Remove hint from extension */
 static int ast_remove_hint(struct ast_exten *e)
@@ -5572,6 +3948,7 @@ static int ast_add_hint(struct ast_exten *e)
        if (!hint_new) {
                return -1;
        }
+       AST_VECTOR_INIT(&hint_new->devices, 8);
 
        /* Initialize new hint. */
        hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
@@ -5580,12 +3957,17 @@ static int ast_add_hint(struct ast_exten *e)
                return -1;
        }
        hint_new->exten = e;
-       hint_new->laststate = ast_extension_state2(e, NULL);
-       if ((presence_state = extension_presence_state_helper(e, &subtype, &message)) > 0) {
-               hint_new->last_presence_state = presence_state;
-               hint_new->last_presence_subtype = subtype;
-               hint_new->last_presence_message = message;
-               message = subtype = NULL;
+       if (strstr(e->app, "${") && e->exten[0] == '_') {
+               /* The hint is dynamic and hasn't been evaluted yet */
+               hint_new->laststate = AST_DEVICE_INVALID;
+               hint_new->last_presence_state = AST_PRESENCE_INVALID;
+       } else {
+               hint_new->laststate = ast_extension_state2(e, NULL);
+               if ((presence_state = extension_presence_state_helper(e, &subtype, &message)) > 0) {
+                       hint_new->last_presence_state = presence_state;
+                       hint_new->last_presence_subtype = subtype;
+                       hint_new->last_presence_message = message;
+               }
        }
 
        /* Prevent multiple add hints from adding the same hint at the same time. */
@@ -5612,22 +3994,63 @@ static int ast_add_hint(struct ast_exten *e)
                        ast_get_context_name(ast_get_extension_context(e)));
        }
 
+       /* if not dynamic */
+       if (!(strstr(e->app, "${") && e->exten[0] == '_')) {
+               struct ast_state_cb *state_cb;
+               struct ao2_iterator cb_iter;
+
+               /* 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,
+                               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);
+       }
        ao2_unlock(hints);
        ao2_ref(hint_new, -1);
 
        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_hint *hint;
+       struct stasis_message *message;
 
-       if (!oe || !ne) {
+       if (!hint_change_message_type()) {
                return -1;
        }
 
-       ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
+       /* 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;
+       }
+
+       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. */
 
        /*
         * Unlink the hint from the hints container as the extension
@@ -5636,6 +4059,7 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        hint = ao2_find(hints, oe, OBJ_UNLINK);
        if (!hint) {
                ao2_unlock(hints);
+               ast_mutex_unlock(&context_merge_lock);
                return -1;
        }
 
@@ -5644,21 +4068,24 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
        /* Update the hint and put it back in the hints container. */
        ao2_lock(hint);
        hint->exten = ne;
+
        ao2_unlock(hint);
+
        ao2_link(hints, hint);
        if (add_hintdevice(hint, ast_get_extension_app(ne))) {
                ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
                        ast_get_extension_name(ne),
                        ast_get_context_name(ast_get_extension_context(ne)));
        }
-
        ao2_unlock(hints);
+
+       publish_hint_change(hint, ne);
+
        ao2_ref(hint, -1);
 
        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)
 {
@@ -5736,6 +4163,12 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
 
        ast_channel_lock(chan);
 
+       /*
+        * Make sure that the channel is marked as hungup since we are
+        * going to run the h exten on it.
+        */
+       ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
+
        /* Set h exten location */
        if (context != ast_channel_context(chan)) {
                ast_channel_context_set(chan, context);
@@ -5743,12 +4176,6 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
        ast_channel_exten_set(chan, "h");
        ast_channel_priority_set(chan, 1);
 
-       /*
-        * Make sure that the channel is marked as hungup since we are
-        * going to run the h exten on it.
-        */
-       ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
-
        /* Save autoloop flag */
        autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
        ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
@@ -5786,250 +4213,8 @@ 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 */
-static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
+void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
 {
        ast_channel_lock(c);
        ast_channel_exten_set(c, exten);
@@ -6079,7 +4264,7 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
        int autoloopflag;
        int error = 0;          /* set an error conditions */
        struct ast_pbx *pbx;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        /* A little initial setup here */
        if (ast_channel_pbx(c)) {
@@ -6101,14 +4286,13 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
                if (!callid) {
                        callid = ast_create_callid();
                        if (callid) {
+                               ast_channel_lock(c);
                                ast_channel_callid_set(c, callid);
+                               ast_channel_unlock(c);
                        }
                }
                ast_callid_threadassoc_add(callid);
-               callid = ast_callid_unref(callid);
-       } else {
-               /* Nothing to do here, The thread is already bound to a callid.  Let's just get rid of the reference. */
-               ast_callid_unref(callid);
+               callid = 0;
        }
 
        ast_channel_pbx_set(c, pbx);
@@ -6116,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  */
@@ -6143,6 +4329,7 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
                while (!(res = ast_spawn_extension(c, ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c),
                        S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL),
                        &found, 1))) {
+
                        if (!ast_check_hangup(c)) {
                                ast_channel_priority_set(c, ast_channel_priority(c) + 1);
                                continue;
@@ -6289,11 +4476,11 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
                                        status = "UNKNOWN";
                                ast_verb(3, "Auto fallthrough, channel '%s' status is '%s'\n", ast_channel_name(c), status);
                                if (!strcasecmp(status, "CONGESTION"))
-                                       res = pbx_builtin_congestion(c, "10");
+                                       res = indicate_congestion(c, "10");
                                else if (!strcasecmp(status, "CHANUNAVAIL"))
-                                       res = pbx_builtin_congestion(c, "10");
+                                       res = indicate_congestion(c, "10");
                                else if (!strcasecmp(status, "BUSY"))
-                                       res = pbx_builtin_busy(c, "10");
+                                       res = indicate_busy(c, "10");
                                error = 1; /* XXX disable message */
                                break;  /* exit from the 'for' loop */
                        }
@@ -6361,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);
 
@@ -6620,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;
                }
@@ -6677,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);
 
@@ -6743,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;
@@ -6758,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) {
@@ -6783,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);
@@ -6798,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 */
@@ -6810,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 {
@@ -6827,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 */
@@ -6841,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) &&
@@ -6933,296 +5144,72 @@ 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)
+/*
+ * Help for CLI commands ...
+ */
+
+/*! \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)
 {
-       struct ast_app *tmp;
-       struct ast_app *cur;
-       int length;
-#ifdef AST_XML_DOCS
-       char *tmpxml;
-#endif
+       struct ast_hint *hint;
+       int num = 0;
+       int watchers;
+       struct ao2_iterator i;
+       char buf[AST_MAX_EXTENSION+AST_MAX_CONTEXT+2];
 
-       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;
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "core show hints";
+               e->usage =
+                       "Usage: core show hints\n"
+                       "       List registered hints.\n"
+                       "       Hint details are shown in five columns. In order from left to right, they are:\n"
+                       "       1. Hint extension URI.\n"
+                       "       2. List of mapped device or presence state identifiers.\n"
+                       "       3. Current extension state. The aggregate of mapped device states.\n"
+                       "       4. Current presence state for the mapped presence state provider.\n"
+                       "       5. Watchers - number of subscriptions and other entities watching this hint.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
        }
 
-       length = sizeof(*tmp) + strlen(app) + 1;
-
-       if (!(tmp = ast_calloc(1, length))) {
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
+       if (ao2_container_count(hints) == 0) {
+               ast_cli(a->fd, "There are no registered dialplan hints\n");
+               return CLI_SUCCESS;
        }
+       /* ... we have hints ... */
+       ast_cli(a->fd, "\n    -= Registered Asterisk Dial Plan Hints =-\n");
 
-       if (ast_string_field_init(tmp, 128)) {
-               AST_RWLIST_UNLOCK(&apps);
-               ast_free(tmp);
-               return -1;
-       }
+       i = ao2_iterator_init(hints, 0);
+       for (; (hint = ao2_iterator_next(&i)); ao2_ref(hint, -1)) {
+               ao2_lock(hint);
+               if (!hint->exten) {
+                       /* The extension has already been destroyed */
+                       ao2_unlock(hint);
+                       continue;
+               }
+               watchers = ao2_container_count(hint->callbacks);
+               snprintf(buf, sizeof(buf), "%s@%s",
+                       ast_get_extension_name(hint->exten),
+                       ast_get_context_name(ast_get_extension_context(hint->exten)));
 
-       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
+               ast_cli(a->fd, "%-20.20s: %-20.20s  State:%-15.15s Presence:%-15.15s Watchers %2d\n",
+                       buf,
+                       ast_get_extension_app(hint->exten),
+                       ast_extension_state2str(hint->laststate),
+                       ast_presence_state2str(hint->last_presence_state),
+                       watchers);
 
-       /* 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;
-               }
+               ao2_unlock(hint);
+               num++;
        }
-       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));
+       ao2_iterator_destroy(&i);
 
-       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)
-{
-       struct ast_hint *hint;
-       int num = 0;
-       int watchers;
-       struct ao2_iterator i;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show hints";
-               e->usage =
-                       "Usage: core show hints\n"
-                       "       List registered hints\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (ao2_container_count(hints) == 0) {
-               ast_cli(a->fd, "There are no registered dialplan hints\n");
-               return CLI_SUCCESS;
-       }
-       /* ... we have hints ... */
-       ast_cli(a->fd, "\n    -= Registered Asterisk Dial Plan Hints =-\n");
-
-       i = ao2_iterator_init(hints, 0);
-       for (; (hint = ao2_iterator_next(&i)); ao2_ref(hint, -1)) {
-               ao2_lock(hint);
-               if (!hint->exten) {
-                       /* The extension has already been destroyed */
-                       ao2_unlock(hint);
-                       continue;
-               }
-               watchers = ao2_container_count(hint->callbacks);
-               ast_cli(a->fd, "   %20s@%-20.20s: %-20.20s  State:%-15.15s Watchers %2d\n",
-                       ast_get_extension_name(hint->exten),
-                       ast_get_context_name(ast_get_extension_context(hint->exten)),
-                       ast_get_extension_app(hint->exten),
-                       ast_extension_state2str(hint->laststate), watchers);
-               ao2_unlock(hint);
-               num++;
-       }
-       ao2_iterator_destroy(&i);
-
-       ast_cli(a->fd, "----------------\n");
-       ast_cli(a->fd, "- %d hints registered\n", num);
-       return CLI_SUCCESS;
-}
+       ast_cli(a->fd, "----------------\n");
+       ast_cli(a->fd, "- %d hints registered\n", num);
+       return CLI_SUCCESS;
+}
 
 /*! \brief autocomplete for CLI command 'core show hint' */
 static char *complete_core_show_hint(const char *line, const char *word, int pos, int state)
@@ -7267,13 +5254,20 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        int watchers;
        int num = 0, extenlen;
        struct ao2_iterator i;
+       char buf[AST_MAX_EXTENSION+AST_MAX_CONTEXT+2];
 
        switch (cmd) {
        case CLI_INIT:
                e->command = "core show hint";
                e->usage =
                        "Usage: core show hint <exten>\n"
-                       "       List registered hint\n";
+                       "       List registered hint.\n"
+                       "       Hint details are shown in five columns. In order from left to right, they are:\n"
+                       "       1. Hint extension URI.\n"
+                       "       2. List of mapped device or presence state identifiers.\n"
+                       "       3. Current extension state. The aggregate of mapped device states.\n"
+                       "       4. Current presence state for the mapped presence state provider.\n"
+                       "       5. Watchers - number of subscriptions and other entities watching this hint.\n";
                return NULL;
        case CLI_GENERATE:
                return complete_core_show_hint(a->line, a->word, a->pos, a->n);
@@ -7298,11 +5292,15 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                }
                if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) {
                        watchers = ao2_container_count(hint->callbacks);
-                       ast_cli(a->fd, "   %20s@%-20.20s: %-20.20s  State:%-15.15s Watchers %2d\n",
+                       sprintf(buf, "%s@%s",
                                ast_get_extension_name(hint->exten),
-                               ast_get_context_name(ast_get_extension_context(hint->exten)),
+                               ast_get_context_name(ast_get_extension_context(hint->exten)));
+                       ast_cli(a->fd, "%-20.20s: %-20.20s  State:%-15.15s Presence:%-15.15s Watchers %2d\n",
+                               buf,
                                ast_get_extension_app(hint->exten),
-                               ast_extension_state2str(hint->laststate), watchers);
+                               ast_extension_state2str(hint->laststate), 
+                               ast_presence_state2str(hint->last_presence_state), 
+                               watchers);
                        num++;
                }
                ao2_unlock(hint);
@@ -7315,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.
@@ -7424,89 +5388,6 @@ static char *handle_eat_memory(struct ast_cli_entry *e, int cmd, struct ast_cli_
 }
 #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>");
-               }
-       }
-       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);
-
-       return CLI_SUCCESS;
-}
-
 /*
  * 'show dialplan' CLI command implementation functions ...
  */
@@ -7563,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;
@@ -7573,10 +5469,13 @@ 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
                char buf[256], buf2[256];
+#endif
                int context_info_printed = 0;
 
                if (context && strcmp(ast_get_context_name(c), context))
@@ -7596,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;
                }
 
@@ -7618,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;
                        }
@@ -7631,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 */
@@ -7646,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 */
@@ -7682,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)) {
@@ -7694,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));
@@ -7905,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;
@@ -7926,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 */
@@ -7983,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);
@@ -7998,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];
 
@@ -8013,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);
@@ -8042,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 */
@@ -8057,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];
@@ -8081,47 +6007,18 @@ static int manager_show_dialplan(struct mansession *s, const struct message *m)
                manager_dpsendack(s, m);
        }
 
-       astman_append(s, "Event: ShowDialPlanComplete\r\n"
-               "EventList: Complete\r\n"
-               "ListItems: %d\r\n"
+       astman_send_list_complete_start(s, m, "ShowDialPlanComplete", counters.total_items);
+       astman_append(s,
                "ListExtensions: %d\r\n"
                "ListPriorities: %d\r\n"
-               "ListContexts: %d\r\n"
-               "%s"
-               "\r\n", counters.total_items, counters.total_exten, counters.total_prio, counters.total_context, idtext);
+               "ListContexts: %d\r\n",
+               counters.total_exten, counters.total_prio, counters.total_context);
+       astman_send_list_complete_end(s);
 
        /* everything ok */
        return 0;
 }
 
-/*! \brief CLI support for listing global variables in a parseable way */
-static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       int i = 0;
-       struct ast_var_t *newvariable;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "dialplan show globals";
-               e->usage =
-                       "Usage: dialplan show globals\n"
-                       "       List current global dialplan variables and their values\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       ast_rwlock_rdlock(&globalslock);
-       AST_LIST_TRAVERSE (&globals, newvariable, entries) {
-               i++;
-               ast_cli(a->fd, "   %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
-       }
-       ast_rwlock_unlock(&globalslock);
-       ast_cli(a->fd, "\n    -- %d variable(s)\n", i);
-
-       return CLI_SUCCESS;
-}
-
 #ifdef AST_DEVMODE
 static char *handle_show_device2extenstate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -8152,188 +6049,84 @@ static char *handle_show_device2extenstate(struct ast_cli_entry *e, int cmd, str
 }
 #endif
 
-/*! \brief CLI support for listing chanvar's variables in a parseable way */
-static char *handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-       struct ast_channel_snapshot *snapshot;
-       struct ast_var_t *var;
+       int oldval = 0;
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "dialplan show chanvar";
+               e->command = "dialplan set extenpatternmatchnew true";
                e->usage =
-                       "Usage: dialplan show chanvar <channel>\n"
-                       "       List current channel variables and their values\n";
+                       "Usage: dialplan set extenpatternmatchnew true|false\n"
+                       "       Use the NEW extension pattern matching algorithm, true or false.\n";
                return NULL;
        case CLI_GENERATE:
-               return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+               return NULL;
        }
 
-       if (a->argc != e->args + 1)
+       if (a->argc != 4)
                return CLI_SHOWUSAGE;
 
-       if (!(msg = stasis_cache_get(ast_channel_cache_by_name(), ast_channel_snapshot_type(), a->argv[3]))) {
-               ast_cli(a->fd, "Channel '%s' not found\n", a->argv[e->args]);
-               return CLI_FAILURE;
-       }
-       snapshot = stasis_message_data(msg);
+       oldval =  pbx_set_extenpatternmatchnew(1);
 
-       AST_LIST_TRAVERSE(snapshot->channel_vars, var, entries) {
-               ast_cli(a->fd, "%s=%s\n", ast_var_name(var), ast_var_value(var));
-       }
+       if (oldval)
+               ast_cli(a->fd, "\n    -- Still using the NEW pattern match algorithm for extension names in the dialplan.\n");
+       else
+               ast_cli(a->fd, "\n    -- Switched to using the NEW pattern match algorithm for extension names in the dialplan.\n");
 
        return CLI_SUCCESS;
 }
 
-static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
+       int oldval = 0;
+
        switch (cmd) {
        case CLI_INIT:
-               e->command = "dialplan set global";
+               e->command = "dialplan set extenpatternmatchnew false";
                e->usage =
-                       "Usage: dialplan set global <name> <value>\n"
-                       "       Set global dialplan variable <name> to <value>\n";
+                       "Usage: dialplan set extenpatternmatchnew true|false\n"
+                       "       Use the NEW extension pattern matching algorithm, true or false.\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
        }
 
-       if (a->argc != e->args + 2)
+       if (a->argc != 4)
                return CLI_SHOWUSAGE;
 
-       pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
-       ast_cli(a->fd, "\n    -- Global variable '%s' set to '%s'\n", a->argv[3], a->argv[4]);
+       oldval =  pbx_set_extenpatternmatchnew(0);
+
+       if (!oldval)
+               ast_cli(a->fd, "\n    -- Still using the OLD pattern match algorithm for extension names in the dialplan.\n");
+       else
+               ast_cli(a->fd, "\n    -- Switched to using the OLD pattern match algorithm for extension names in the dialplan.\n");
 
        return CLI_SUCCESS;
 }
 
-static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+/*
+ * CLI entries for upper commands ...
+ */
+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_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_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."),
+};
+
+void unreference_cached_app(struct ast_app *app)
 {
-       struct ast_channel *chan;
-       const char *chan_name, *var_name, *var_value;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "dialplan set chanvar";
-               e->usage =
-                       "Usage: dialplan set chanvar <channel> <varname> <value>\n"
-                       "       Set channel variable <varname> to <value>\n";
-               return NULL;
-       case CLI_GENERATE:
-               return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
-       }
-
-       if (a->argc != e->args + 3)
-               return CLI_SHOWUSAGE;
-
-       chan_name = a->argv[e->args];
-       var_name = a->argv[e->args + 1];
-       var_value = a->argv[e->args + 2];
-
-       if (!(chan = ast_channel_get_by_name(chan_name))) {
-               ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
-               return CLI_FAILURE;
-       }
-
-       pbx_builtin_setvar_helper(chan, var_name, var_value);
-
-       chan = ast_channel_unref(chan);
-
-       ast_cli(a->fd, "\n    -- Channel variable '%s' set to '%s' for '%s'\n",  var_name, var_value, chan_name);
-
-       return CLI_SUCCESS;
-}
-
-static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       int oldval = 0;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "dialplan set extenpatternmatchnew true";
-               e->usage =
-                       "Usage: dialplan set extenpatternmatchnew true|false\n"
-                       "       Use the NEW extension pattern matching algorithm, true or false.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (a->argc != 4)
-               return CLI_SHOWUSAGE;
-
-       oldval =  pbx_set_extenpatternmatchnew(1);
-
-       if (oldval)
-               ast_cli(a->fd, "\n    -- Still using the NEW pattern match algorithm for extension names in the dialplan.\n");
-       else
-               ast_cli(a->fd, "\n    -- Switched to using the NEW pattern match algorithm for extension names in the dialplan.\n");
-
-       return CLI_SUCCESS;
-}
-
-static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       int oldval = 0;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "dialplan set extenpatternmatchnew false";
-               e->usage =
-                       "Usage: dialplan set extenpatternmatchnew true|false\n"
-                       "       Use the NEW extension pattern matching algorithm, true or false.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (a->argc != 4)
-               return CLI_SHOWUSAGE;
-
-       oldval =  pbx_set_extenpatternmatchnew(0);
-
-       if (!oldval)
-               ast_cli(a->fd, "\n    -- Still using the OLD pattern match algorithm for extension names in the dialplan.\n");
-       else
-               ast_cli(a->fd, "\n    -- Switched to using the OLD pattern match algorithm for extension names in the dialplan.\n");
-
-       return CLI_SUCCESS;
-}
-
-/*
- * CLI entries for upper commands ...
- */
-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_functions, "Shows registered dialplan functions"),
-       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"),
-       AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables"),
-#ifdef AST_DEVMODE
-       AST_CLI_DEFINE(handle_show_device2extenstate, "Show expected exten state from multiple device states"),
-#endif
-       AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
-       AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
-       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_set_global, "Set global dialplan variable"),
-       AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),
-       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)
-{
-       struct ast_context *context = NULL;
-       struct ast_exten *eroot = NULL, *e = NULL;
+       struct ast_context *context = NULL;
+       struct ast_exten *eroot = NULL, *e = NULL;
 
        ast_rdlock_contexts();
        while ((context = ast_walk_contexts(context))) {
@@ -8349,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;
@@ -8404,9 +6167,9 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
                ast_rdlock_contexts();
                local_contexts = &contexts;
                tmp = ast_hashtab_lookup(contexts_table, &search);
-               ast_unlock_contexts();
                if (tmp) {
                        tmp->refcount++;
+                       ast_unlock_contexts();
                        return tmp;
                }
        } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */
@@ -8425,21 +6188,23 @@ 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);
+               if (!extcontexts) {
+                       ast_unlock_contexts();
+               }
                return NULL;
        }
 
        if (!extcontexts) {
-               ast_wrlock_contexts();
                tmp->next = *local_contexts;
                *local_contexts = tmp;
                ast_hashtab_insert_safe(contexts_table, tmp); /*put this context into the tree */
                ast_unlock_contexts();
-               ast_debug(1, "Registered context '%s'(%p) in table %p registrar: %s\n", tmp->name, tmp, contexts_table, registrar);
                ast_verb(3, "Registered extension context '%s'; registrar: %s\n", tmp->name, registrar);
        } else {
                tmp->next = *local_contexts;
@@ -8447,12 +6212,16 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
                        ast_hashtab_insert_immediate(exttable, tmp); /*put this context into the tree */
 
                *local_contexts = tmp;
-               ast_debug(1, "Registered context '%s'(%p) in local table %p; registrar: %s\n", tmp->name, tmp, exttable, registrar);
                ast_verb(3, "Registered extension context '%s'; registrar: %s\n", tmp->name, registrar);
        }
        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 {
@@ -8472,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 */
@@ -8541,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 */
@@ -8560,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, prio_item->datad, 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);