loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / pbx.c
index c3fc851..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,628 +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>.</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.</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.</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.</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>.</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.
@@ -791,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
@@ -801,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;
 
@@ -840,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  */
@@ -854,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
 {
@@ -909,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. */
@@ -987,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);
@@ -1018,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
@@ -1035,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;
 }
 
@@ -1098,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");
        }
@@ -1142,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);
@@ -1192,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
@@ -1237,13 +699,22 @@ static int hashtab_compare_extens(const void *ah_a, const void *ah_b)
        }
 
        /* but if they are the same, do the cidmatch values match? */
-       if (ac->matchcid && bc->matchcid) {
-               return strcmp(ac->cidmatch,bc->cidmatch);
-       } else if (!ac->matchcid && !bc->matchcid) {
-               return 0; /* if there's no matchcid on either side, then this is a match */
-       } else {
-               return 1; /* if there's matchcid on one but not the other, they are different */
+       /* not sure which side may be using ast_ext_matchcid_types, so check both */
+       if (ac->matchcid == AST_EXT_MATCHCID_ANY || bc->matchcid == AST_EXT_MATCHCID_ANY) {
+               return 0;
+       }
+       if (ac->matchcid == AST_EXT_MATCHCID_OFF && bc->matchcid == AST_EXT_MATCHCID_OFF) {
+               return 0;
+       }
+       if (ac->matchcid != bc->matchcid) {
+               return 1;
+       }
+       /* all other cases already disposed of, match now required on callerid string (cidmatch) */
+       /* although ast_add_extension2_lockopt() enforces non-zero ptr, caller may not have */
+       if (ast_strlen_zero(ac->cidmatch) && ast_strlen_zero(bc->cidmatch)) {
+               return 0;
        }
+       return strcmp(ac->cidmatch, bc->cidmatch);
 }
 
 static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b)
@@ -1271,7 +742,7 @@ static unsigned int hashtab_hash_extens(const void *obj)
        const struct ast_exten *ac = obj;
        unsigned int x = ast_hashtab_hash_string(ac->exten);
        unsigned int y = 0;
-       if (ac->matchcid)
+       if (ac->matchcid == AST_EXT_MATCHCID_ON)
                y = ast_hashtab_hash_string(ac->cidmatch);
        return x+y;
 }
@@ -1288,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;
@@ -1305,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;
 
@@ -1365,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
@@ -1467,10 +881,14 @@ int check_contexts(char *file, int line )
                                ast_copy_string(dummy_name, e1->exten, sizeof(dummy_name));
                                e2 = ast_hashtab_lookup(c1->root_table, &ex);
                                if (!e2) {
-                                       if (e1->matchcid) {
-                                               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 );
+                                       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_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();
                                }
@@ -1563,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);
@@ -1793,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);
@@ -1912,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 */           \
                                                                }                                                                                                        \
                                                        }                                                                                                            \
@@ -1925,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 */                                                 \
                                                        }                                                                                                                                                \
@@ -1969,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 */
                                        }
                                }
@@ -1991,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 */
                                        }
                                }
@@ -2007,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 */
                                        }
                                }
@@ -2360,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;
@@ -2386,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;
@@ -2793,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;
@@ -3035,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;
@@ -3042,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];
 };
@@ -3101,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;
@@ -3325,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 */
@@ -3351,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 {
@@ -3371,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
@@ -3386,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;
@@ -3694,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;
@@ -3720,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);
@@ -3751,7794 +2838,5544 @@ 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;
-       }
+       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);
 
-       ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
+       ast_rdlock_contexts();
+       if (found)
+               *found = 0;
 
-       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, ""));
+       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);
+                               }
+                       }
+                       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);
-
-       ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
 
-       return CLI_SUCCESS;
+               return (matching_action) ? 0 : -1;
+       }
 }
 
-static char *complete_functions(const char *word, int pos, int state)
+/*! \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 *cur;
-       char *ret = NULL;
-       int which = 0;
-       int wordlen;
-       int cmp;
+       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 (pos != 3) {
-               return NULL;
-       }
+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;
+}
 
-       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;
-                       }
-                       ret = ast_strdup(cur->name);
-                       break;
-               }
+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;
        }
-       AST_RWLIST_UNLOCK(&acf_root);
 
-       return ret;
+       return AST_EXTENSION_NOT_INUSE;
 }
 
-static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+/*!
+ * \internal
+ * \brief Parse out the presence portion of the hint string
+ */
+static char *parse_hint_presence(struct ast_str *hint_args)
 {
-       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;
+       char *copy = ast_strdupa(ast_str_buffer(hint_args));
+       char *tmp = "";
 
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show function";
-               e->usage =
-                       "Usage: core show function <function>\n"
-                       "       Describe a particular dialplan function.\n";
+       if ((tmp = strrchr(copy, ','))) {
+               *tmp = '\0';
+               tmp++;
+       } else {
                return NULL;
-       case CLI_GENERATE:
-               return complete_functions(a->word, a->pos, a->n);
        }
+       ast_str_set(&hint_args, 0, "%s", tmp);
+       return ast_str_buffer(hint_args);
+}
 
-       if (a->argc != 4) {
-               return CLI_SHOWUSAGE;
-       }
+/*!
+ * \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;
 
-       if (!(acf = ast_custom_function_find(a->argv[3]))) {
-               ast_cli(a->fd, "No function by that name registered.\n");
-               return CLI_FAILURE;
+       if ((tmp = strrchr(copy, ','))) {
+               *tmp = '\0';
        }
 
-       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;
-       }
+       ast_str_set(&hint_args, 0, "%s", copy);
+       return ast_str_buffer(hint_args);
+}
 
-       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);
+static void device_state_info_dt(void *obj)
+{
+       struct ast_device_state_info *info = obj;
 
-               description_size = strlen(S_OR(acf->desc, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               description = ast_malloc(description_size);
+       ao2_cleanup(info->causing_channel);
+}
 
-               arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               arguments = ast_malloc(arguments_size);
+static struct ao2_container *alloc_device_state_info(void)
+{
+       return ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+}
 
-               seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               seealso = ast_malloc(seealso_size);
+static int ast_extension_state3(struct ast_str *hint_app, struct ao2_container *device_state_info)
+{
+       char *cur;
+       char *rest;
+       struct ast_devstate_aggregate agg;
 
-               /* 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;
-               }
+       /* One or more devices separated with a & character */
+       rest = parse_hint_device(hint_app);
 
-               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);
-       }
+       ast_devstate_aggregate_init(&agg);
+       while ((cur = strsep(&rest, "&"))) {
+               enum ast_device_state state = ast_device_state(cur);
 
-       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_devstate_aggregate_add(&agg, state);
+               if (device_state_info) {
+                       struct ast_device_state_info *obj;
 
-       ast_free(arguments);
-       ast_free(synopsis);
-       ast_free(description);
-       ast_free(seealso);
-       ast_free(syntax);
+                       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);
+                       }
+               }
+       }
 
-       return CLI_SUCCESS;
+       return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
 }
 
-static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
+/*! \brief Check state of extension by using hints */
+static int ast_extension_state2(struct ast_exten *e, struct ao2_container *device_state_info)
 {
-       struct ast_custom_function *cur;
-       int cmp;
+       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
 
-       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 (!e || !hint_app) {
+               return -1;
        }
 
-       return cur;
+       ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
+       return ast_extension_state3(hint_app, device_state_info);
 }
 
-struct ast_custom_function *ast_custom_function_find(const char *name)
+/*! \brief Return extension_state as string */
+const char *ast_extension_state2str(int extension_state)
 {
-       struct ast_custom_function *acf;
-
-       AST_RWLIST_RDLOCK(&acf_root);
-       acf = ast_custom_function_find_nolock(name);
-       AST_RWLIST_UNLOCK(&acf_root);
+       int i;
 
-       return acf;
+       for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
+               if (extension_states[i].extension_state == extension_state)
+                       return extension_states[i].text;
+       }
+       return "Unknown";
 }
 
-int ast_custom_function_unregister(struct ast_custom_function *acf)
+/*!
+ * \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_custom_function *cur;
+       struct ast_exten *e;
 
-       if (!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);
-       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);
+       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;
                }
-#endif
-               ast_verb(2, "Unregistered custom function %s\n", cur->name);
        }
-       AST_RWLIST_UNLOCK(&acf_root);
 
-       return cur ? 0 : -1;
+       return ast_extension_state2(e, device_state_info);  /* Check all devices in the hint */
 }
 
-/*! \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 extension state for an extension by using hint */
+int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
 {
-#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;
-       }
+       return internal_extension_state_extended(c, context, exten, NULL);
+}
 
-       if (ast_string_field_init(acf, 128)) {
-               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);
+/*! \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;
 
-       /* 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);
+       if (device_state_info) {
+               container = alloc_device_state_info();
+       }
 
-       /* 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);
+       ret = internal_extension_state_extended(c, context, exten, container);
+       if (ret < 0 && container) {
+               ao2_ref(container, -1);
+               container = NULL;
+       }
 
-       acf->docsrc = AST_XML_DOC;
-#endif
+       if (device_state_info) {
+               get_device_state_causing_channels(container);
+               *device_state_info = container;
+       }
 
-       return 0;
+       return ret;
 }
 
-int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
+static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
 {
-       struct ast_custom_function *cur;
+       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+       char *presence_provider;
+       const char *app;
 
-       if (!acf) {
+       if (!e || !hint_app) {
                return -1;
        }
 
-       acf->mod = mod;
-#ifdef AST_XML_DOCS
-       acf->docsrc = AST_STATIC_DOC;
-#endif
-
-       if (acf_retrieve_docs(acf)) {
+       app = ast_get_extension_app(e);
+       if (ast_strlen_zero(app)) {
                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;
-       }
+       ast_str_set(&hint_app, 0, "%s", app);
+       presence_provider = parse_hint_presence(hint_app);
 
-       /* 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;
-               }
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       if (!cur) {
-               AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
+       if (ast_strlen_zero(presence_provider)) {
+               /* No presence string in the hint */
+               return 0;
        }
 
-       AST_RWLIST_UNLOCK(&acf_root);
-
-       ast_verb(2, "Registered custom function '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, acf->name));
-
-       return 0;
+       return ast_presence_state(presence_provider, subtype, message);
 }
 
-/*! \brief return a pointer to the arguments of the function,
- * and terminates the function name with '\\0'
- */
-static char *func_args(char *function)
+int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
 {
-       char *args = strchr(function, '(');
+       struct ast_exten *e;
 
-       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);
-               }
+       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
+               return -1;                   /* No hint, return -1 */
        }
-       return args;
-}
-
-int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
-{
-       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->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);
+       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_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
-               ast_free(str);
-               return res;
        }
-       return -1;
+
+       return extension_presence_state_helper(e, subtype, message);
 }
 
-int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
+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)
 {
-       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;
+       int res = 0;
+       struct ast_state_cb_info info = { 0, };
 
-       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);
+       info.reason = reason;
+
+       /* 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 {
-                       /* 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);
+                       info.presence_subtype = "";
                }
-               if (acfptr->mod && u) {
-                       __ast_module_user_remove(acfptr->mod, u);
+               if (!(ast_strlen_zero(hint->last_presence_message))) {
+                       info.presence_message = ast_strdupa(hint->last_presence_message);
+               } else {
+                       info.presence_message = "";
                }
-               return res;
+               ao2_unlock(hint);
+       } else {
+               info.exten_state = AST_EXTENSION_REMOVED;
        }
-       return -1;
+
+       res = cb(context, exten, &info, data);
+
+       return res;
 }
 
-int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+/*!
+ * /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)
 {
-       char *copy = ast_strdupa(function);
-       char *args = func_args(copy);
-       struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+       struct ao2_iterator iter;
+       struct ast_device_state_info *info;
+       struct ast_channel *chan;
 
-       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 (!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 */
 
-       return -1;
-}
+               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;
+               }
 
-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;
+               /* 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;
-                       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++;
+                       /* 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;
                        }
-                       if (brackets)
-                               ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
-                       len = vare - vars - 1;
-
-                       /* Skip totally over variable string */
-                       whereweare += (len + 3);
+                       ast_channel_unlock(chan);
+               }
+               ast_channel_iterator_destroy(chan_iter);
+       }
+       ao2_iterator_destroy(&iter);
+}
 
-                       /* 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);
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
+{
+       struct ao2_iterator cb_iter;
+       struct ast_state_cb *state_cb;
+       int state;
+       int same_state;
+       struct ao2_container *device_state_info;
+       int first_extended_cb_call = 1;
+       char context_name[AST_MAX_CONTEXT];
+       char exten_name[AST_MAX_EXTENSION];
 
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
+       ao2_lock(hint);
+       if (!hint->exten) {
+               /* The extension has already been destroyed */
+               ao2_unlock(hint);
+               return;
+       }
 
-                               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);
-                       }
+       /*
+        * 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);
 
-                       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 result is '%s'\n", 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;
+       /*
+        * 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)
+        */
 
-                       /* Skip totally over expression */
-                       whereweare += (len + 3);
+       /* 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();
 
-                       /* Store variable name (and truncate) */
-                       ast_str_set_substr(&substr1, 0, vars, len);
+       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;
+       }
 
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
+       /* Device state changed since last check - notify the watchers. */
+       hint->laststate = state;        /* record we saw the change */
 
-                               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);
-                       }
+       /* 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);
+       }
 
-                       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));
+       /* 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);
                }
        }
-       *used = ast_str_strlen(*buf) - orig_size;
-       ast_free(substr1);
-       ast_free(substr2);
-       ast_free(substr3);
+       ao2_iterator_destroy(&cb_iter);
+
+       ao2_cleanup(device_state_info);
 }
 
-void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
+static void presence_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app,
+                                           struct ast_presence_state_message *presence_state)
 {
-       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);
-}
-
-void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
-{
-       /* 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);
+       struct ao2_iterator cb_iter;
+       struct ast_state_cb *state_cb;
+       char context_name[AST_MAX_CONTEXT];
+       char exten_name[AST_MAX_EXTENSION];
 
-                       if (!var)
-                               var = ast_alloca(VAR_BUF_SIZE);
+       ao2_lock(hint);
+       if (!hint->exten) {
+               /* The extension has already been destroyed */
+               ao2_unlock(hint);
+               return;
+       }
 
-                       /* Store variable name (and truncate) */
-                       ast_copy_string(var, vars, len + 1);
+       /*
+        * 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);
 
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
+       /* 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;
+       }
 
-                               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;
-                       }
+       /* update new values */
+       ast_free(hint->last_presence_subtype);
+       ast_free(hint->last_presence_message);
+       hint->last_presence_state = presence_state->state;
+       hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
+       hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
+
+       /* For general callbacks */
+       cb_iter = ao2_iterator_init(statecbs, 0);
+       for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+               execute_state_callback(state_cb->change_cb,
+                       context_name,
+                       exten_name,
+                       state_cb->data,
+                       AST_HINT_UPDATE_PRESENCE,
+                       hint,
+                       NULL);
+       }
+       ao2_iterator_destroy(&cb_iter);
+
+       /* For extension callbacks */
+       cb_iter = ao2_iterator_init(hint->callbacks, 0);
+       for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
+               execute_state_callback(state_cb->change_cb,
+                       context_name,
+                       exten_name,
+                       state_cb->data,
+                       AST_HINT_UPDATE_PRESENCE,
+                       hint,
+                       NULL);
+       }
+       ao2_iterator_destroy(&cb_iter);
+}
+
+static int handle_hint_change_message_type(struct stasis_message *msg, enum ast_state_cb_update_reason reason)
+{
+       struct ast_hint *hint;
+       struct ast_str *hint_app;
 
-                       if (!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 result is '%s'\n", 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;
+       if (hint_change_message_type() != stasis_message_type(msg)) {
+               return 0;
+       }
 
-                       /* Skip totally over expression */
-                       whereweare += (len + 3);
+       if (!(hint_app = ast_str_create(1024))) {
+               return -1;
+       }
 
-                       if (!var)
-                               var = ast_alloca(VAR_BUF_SIZE);
+       hint = stasis_message_data(msg);
 
-                       /* Store variable name (and truncate) */
-                       ast_copy_string(var, vars, len + 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;
 
-                       /* Substitute if necessary */
-                       if (needsub) {
-                               size_t my_used;
+                       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
+                               };
 
-                               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;
+                               presence_state_notify_callbacks(hint, &hint_app, &presence_state);
                        }
 
-                       length = ast_expr(vars, cp2, count, c);
-
-                       if (length) {
-                               ast_debug(1, "Expression result is '%s'\n", cp2);
-                               count -= length;
-                               cp2 += length;
-                               *cp2 = 0;
-                       }
+                       ast_free(presence_subtype);
+                       ast_free(presence_message);
                }
+               break;
        }
-       *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);
+       ast_free(hint_app);
+       return 1;
 }
 
-void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
-       size_t used;
-       pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, &used);
-}
+       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;
 
-static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
-{
-       const char *tmp;
+       if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
+               return;
+       }
 
-       /* Nothing more to do */
-       if (!e->data) {
-               *passdata = '\0';
+       if (ast_device_state_message_type() != stasis_message_type(msg)) {
                return;
        }
 
-       /* 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);
+       dev_state = stasis_message_data(msg);
+       if (dev_state->eid) {
+               /* ignore non-aggregate states */
                return;
        }
 
-       pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
-}
+       if (ao2_container_count(hintdevices) == 0 && ao2_container_count(autohints) == 0) {
+               /* There are no hints monitoring devices. */
+               return;
+       }
 
-/*!
- * \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_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);
+       hint_app = ast_str_create(1024);
+       if (!hint_app) {
+               return;
+       }
 
-       ast_rdlock_contexts();
-       if (found)
-               *found = 0;
+       cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(dev_state->device));
+       strcpy(cmpdevice->hintdevice, dev_state->device);
 
-       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;
+       ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+
+       /* Initially we find all hints for the device and notify them */
+       dev_iter = ao2_t_callback(hintdevices,
+               OBJ_SEARCH_OBJECT | OBJ_MULTIPLE,
+               hintdevice_cmp_multiple,
+               cmpdevice,
+               "find devices in container");
+       if (dev_iter) {
+               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);
                        }
-                       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");
                }
+               ao2_iterator_destroy(dev_iter);
+       }
 
-               return (matching_action) ? 0 : -1;
+       /* Second stage we look for any autohint contexts and if the device is not already in the hints
+        * we create it.
+        */
+       type = ast_strdupa(dev_state->device);
+       if (ast_strlen_zero(type)) {
+               goto end;
        }
-}
 
-/*! \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);
-}
+       /* 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;
+       }
 
-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;
-}
+       /* Invalid device state name - not a virtual/custom device and not a real device */
+       if (ast_strlen_zero(device_name)) {
+               goto end;
+       }
 
-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;
+       *device_name++ = '\0';
+
+       auto_iter = ao2_iterator_init(autohints, 0);
+       for (; (autohint = ao2_iterator_next(&auto_iter)); ao2_t_ref(autohint, -1, "Next autohint")) {
+               if (ast_get_hint(NULL, 0, NULL, 0, NULL, autohint->context, device_name)) {
+                       continue;
+               }
+
+               /* 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);
+
+               /* Since this hint was just created there are no watchers, so we don't need to notify anyone */
        }
+       ao2_iterator_destroy(&auto_iter);
 
-       return AST_EXTENSION_NOT_INUSE;
+end:
+       ast_mutex_unlock(&context_merge_lock);
+       ast_free(hint_app);
+       return;
 }
 
 /*!
  * \internal
- * \brief Parse out the presence portion of the hint string
+ * \brief Destroy the given state callback object.
+ *
+ * \param doomed State callback to destroy.
+ *
+ * \return Nothing
  */
-static char *parse_hint_presence(struct ast_str *hint_args)
+static void destroy_state_cb(void *doomed)
 {
-       char *copy = ast_strdupa(ast_str_buffer(hint_args));
-       char *tmp = "";
+       struct ast_state_cb *state_cb = doomed;
 
-       if ((tmp = strrchr(copy, ','))) {
-               *tmp = '\0';
-               tmp++;
-       } else {
-               return NULL;
+       if (state_cb->destroy_cb) {
+               state_cb->destroy_cb(state_cb->id, state_cb->data);
        }
-       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
+ * \brief Add watcher for extension states with destructor
  */
-static char *parse_hint_device(struct ast_str *hint_args)
+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)
 {
-       char *copy = ast_strdupa(ast_str_buffer(hint_args));
-       char *tmp;
+       struct ast_hint *hint;
+       struct ast_state_cb *state_cb;
+       struct ast_exten *e;
+       int id;
 
-       if ((tmp = strrchr(copy, ','))) {
-               *tmp = '\0';
+       /* 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;
        }
 
-       ast_str_set(&hint_args, 0, "%s", copy);
-       return ast_str_buffer(hint_args);
-}
+       if (!context || !exten)
+               return -1;
 
-static void device_state_info_dt(void *obj)
-{
-       struct ast_device_state_info *info = obj;
+       /* This callback type is for only one hint, so get the hint */
+       e = ast_hint_extension(NULL, context, exten);
+       if (!e) {
+               return -1;
+       }
 
-       ao2_cleanup(info->causing_channel);
+       /* 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;
 }
 
-static struct ao2_container *alloc_device_state_info(void)
+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 ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+       return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 0);
 }
 
-static int ast_extension_state3(struct ast_str *hint_app, struct ao2_container *device_state_info)
+int ast_extension_state_add(const char *context, const char *exten,
+       ast_state_cb_type change_cb, void *data)
 {
-       char *cur;
-       char *rest;
-       struct ast_devstate_aggregate agg;
+       return extension_state_add_destroy(context, exten, change_cb, NULL, data, 0);
+}
 
-       /* One or more devices separated with a & character */
-       rest = parse_hint_device(hint_app);
+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);
+}
 
-       ast_devstate_aggregate_init(&agg);
-       while ((cur = strsep(&rest, "&"))) {
-               enum ast_device_state state = ast_device_state(cur);
+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);
+}
 
-               ast_devstate_aggregate_add(&agg, state);
-               if (device_state_info) {
-                       struct ast_device_state_info *obj;
+/*! \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;
 
-                       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);
-                       }
-               }
+       if ((state_cb = ao2_find(hint->callbacks, id, 0))) {
+               ao2_ref(state_cb, -1);
+               return CMP_MATCH | CMP_STOP;
        }
 
-       return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
+       return 0;
 }
 
-/*! \brief Check state of extension by using hints */
-static int ast_extension_state2(struct ast_exten *e, struct ao2_container *device_state_info)
+int ast_extension_state_del(int id, ast_state_cb_type change_cb)
 {
-       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+       struct ast_state_cb *p_cur;
+       int ret = -1;
 
-       if (!e || !hint_app) {
-               return -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);
        }
 
-       ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
-       return ast_extension_state3(hint_app, device_state_info);
+       return ret;
 }
 
-/*! \brief Return extension_state as string */
-const char *ast_extension_state2str(int extension_state)
+static int hint_id_cmp(void *obj, void *arg, int flags)
 {
-       int i;
+       const struct ast_state_cb *cb = obj;
+       int *id = arg;
 
-       for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
-               if (extension_states[i].extension_state == extension_state)
-                       return extension_states[i].text;
-       }
-       return "Unknown";
+       return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 /*!
  * \internal
- * \brief Check extension state for an extension by using hint
+ * \brief Destroy the given hint object.
+ *
+ * \param obj Hint to destroy.
+ *
+ * \return Nothing
  */
-static int internal_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
-       struct ao2_container *device_state_info)
+static void destroy_hint(void *obj)
 {
-       struct ast_exten *e;
+       struct ast_hint *hint = obj;
+       int i;
 
-       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
-               return -1;                   /* No hint, return -1 */
-       }
+       if (hint->callbacks) {
+               struct ast_state_cb *state_cb;
+               const char *context_name;
+               const char *exten_name;
 
-       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;
+               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);
        }
 
-       return ast_extension_state2(e, device_state_info);  /* Check all devices in the hint */
+       for (i = 0; i < AST_VECTOR_SIZE(&hint->devices); i++) {
+               char *device = AST_VECTOR_GET(&hint->devices, i);
+               ast_free(device);
+       }
+       AST_VECTOR_FREE(&hint->devices);
+       ast_free(hint->last_presence_subtype);
+       ast_free(hint->last_presence_message);
 }
 
-/*! \brief Check extension state for an extension by using hint */
-int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
+/*! \brief Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
 {
-       return internal_extension_state_extended(c, context, exten, NULL);
-}
-
-/*! \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;
+       /* Cleanup the Notifys if hint is removed */
+       struct ast_hint *hint;
 
-       if (device_state_info) {
-               container = alloc_device_state_info();
+       if (!e) {
+               return -1;
        }
 
-       ret = internal_extension_state_extended(c, context, exten, container);
-       if (ret < 0 && container) {
-               ao2_ref(container, -1);
-               container = NULL;
+       hint = ao2_find(hints, e, OBJ_UNLINK);
+       if (!hint) {
+               return -1;
        }
 
-       if (device_state_info) {
-               get_device_state_causing_channels(container);
-               *device_state_info = container;
-       }
+       remove_hintdevice(hint);
 
-       return ret;
+       /*
+        * The extension is being destroyed so we must save some
+        * information to notify that the extension is deactivated.
+        */
+       ao2_lock(hint);
+       ast_copy_string(hint->context_name,
+               ast_get_context_name(ast_get_extension_context(hint->exten)),
+               sizeof(hint->context_name));
+       ast_copy_string(hint->exten_name, ast_get_extension_name(hint->exten),
+               sizeof(hint->exten_name));
+       hint->exten = NULL;
+       ao2_unlock(hint);
+
+       ao2_ref(hint, -1);
+
+       return 0;
 }
 
-static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
+/*! \brief Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
 {
-       struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
-       char *presence_provider;
-       const char *app;
+       struct ast_hint *hint_new;
+       struct ast_hint *hint_found;
+       char *message = NULL;
+       char *subtype = NULL;
+       int presence_state;
 
-       if (!e || !hint_app) {
+       if (!e) {
                return -1;
        }
 
-       app = ast_get_extension_app(e);
-       if (ast_strlen_zero(app)) {
+       /*
+        * We must create the hint we wish to add before determining if
+        * it is already in the hints container to avoid possible
+        * deadlock when getting the current extension state.
+        */
+       hint_new = ao2_alloc(sizeof(*hint_new), destroy_hint);
+       if (!hint_new) {
                return -1;
        }
+       AST_VECTOR_INIT(&hint_new->devices, 8);
 
-       ast_str_set(&hint_app, 0, "%s", app);
-       presence_provider = parse_hint_presence(hint_app);
-
-       if (ast_strlen_zero(presence_provider)) {
-               /* No presence string in the hint */
-               return 0;
+       /* Initialize new hint. */
+       hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
+       if (!hint_new->callbacks) {
+               ao2_ref(hint_new, -1);
+               return -1;
+       }
+       hint_new->exten = e;
+       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;
+               }
        }
 
-       return ast_presence_state(presence_provider, subtype, message);
-}
+       /* Prevent multiple add hints from adding the same hint at the same time. */
+       ao2_lock(hints);
 
-int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
-{
-       struct ast_exten *e;
+       /* Search if hint exists, do nothing */
+       hint_found = ao2_find(hints, e, 0);
+       if (hint_found) {
+               ao2_ref(hint_found, -1);
+               ao2_unlock(hints);
+               ao2_ref(hint_new, -1);
+               ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n",
+                       ast_get_extension_name(e), ast_get_extension_app(e));
+               return -1;
+       }
 
-       if (!(e = ast_hint_extension(c, context, exten))) {  /* Do we have a hint for this extension ? */
-               return -1;                   /* No hint, return -1 */
+       /* Add new hint to the hints container */
+       ast_debug(2, "HINTS: Adding hint %s: %s\n",
+               ast_get_extension_name(e), ast_get_extension_app(e));
+       ao2_link(hints, hint_new);
+       if (add_hintdevice(hint_new, ast_get_extension_app(e))) {
+               ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
+                       ast_get_extension_name(e),
+                       ast_get_context_name(ast_get_extension_context(e)));
        }
 
-       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;
+       /* 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 extension_presence_state_helper(e, subtype, message);
+       return 0;
 }
 
-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)
+/*! \brief Publish a hint changed event  */
+static int publish_hint_change(struct ast_hint *hint, struct ast_exten *ne)
 {
-       int res = 0;
-       struct ast_state_cb_info info = { 0, };
+       struct stasis_message *message;
 
-       info.reason = reason;
+       if (!hint_change_message_type()) {
+               return -1;
+       }
 
-       /* 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;
+       /* 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;
        }
 
-       /* NOTE: The casts will not be needed for v10 and later */
-       res = cb((char *) context, (char *) exten, &info, data);
+       stasis_publish(ast_device_state_topic_all(), message);
+       stasis_publish(ast_presence_state_topic_all(), message);
 
-       return res;
+       ao2_ref(message, -1);
+
+       return 0;
 }
 
-/*!
- * /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 Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
 {
-       struct ao2_iterator iter;
-       struct ast_device_state_info *info;
-       struct ast_channel *chan;
+       struct ast_hint *hint;
 
-       if (!c || !ao2_container_count(c)) {
-               return;
+       if (!oe || !ne) {
+               return -1;
        }
-       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;
-               }
+       ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
 
-               /* 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);
+       /*
+        * Unlink the hint from the hints container as the extension
+        * name (which is the hash value) could change.
+        */
+       hint = ao2_find(hints, oe, OBJ_UNLINK);
+       if (!hint) {
+               ao2_unlock(hints);
+               ast_mutex_unlock(&context_merge_lock);
+               return -1;
        }
-       ao2_iterator_destroy(&iter);
-}
 
-static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
-{
-       struct ast_device_state_message *dev_state;
-       struct ast_hint *hint;
-       struct ast_str *hint_app;
-       struct ast_hintdevice *device;
-       struct ast_hintdevice *cmpdevice;
-       struct ao2_iterator *dev_iter;
-       struct ao2_iterator cb_iter;
-       char context_name[AST_MAX_CONTEXT];
-       char exten_name[AST_MAX_EXTENSION];
-
-       if (ast_device_state_message_type() != stasis_message_type(msg)) {
-               return;
-       }
-
-       dev_state = stasis_message_data(msg);
-       if (dev_state->eid) {
-               /* ignore non-aggregate states */
-               return;
-       }
-
-       if (ao2_container_count(hintdevices) == 0) {
-               /* There are no hints monitoring devices. */
-               return;
-       }
+       remove_hintdevice(hint);
 
-       hint_app = ast_str_create(1024);
-       if (!hint_app) {
-               return;
-       }
+       /* Update the hint and put it back in the hints container. */
+       ao2_lock(hint);
+       hint->exten = ne;
 
-       cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(dev_state->device));
-       strcpy(cmpdevice->hintdevice, dev_state->device);
+       ao2_unlock(hint);
 
-       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;
+       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);
 
-       for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
-               struct ast_state_cb *state_cb;
-               int state;
-               int same_state;
-               struct ao2_container *device_state_info;
-               int first_extended_cb_call = 1;
-
-               if (!device->hint) {
-                       /* Should never happen. */
-                       continue;
-               }
-               hint = device->hint;
-
-               ao2_lock(hint);
-               if (!hint->exten) {
-                       /* The extension has already been destroyed */
-                       ao2_unlock(hint);
-                       continue;
-               }
-
-               /*
-                * Save off strings in case the hint extension gets destroyed
-                * while we are notifying the watchers.
-                */
-               ast_copy_string(context_name,
-                       ast_get_context_name(ast_get_extension_context(hint->exten)),
-                       sizeof(context_name));
-               ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
-                       sizeof(exten_name));
-               ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-               ao2_unlock(hint);
-
-               /*
-                * Get device state for this hint.
-                *
-                * NOTE: We cannot hold any locks while determining the hint
-                * device state or notifying the watchers without causing a
-                * deadlock.  (conlock, hints, and hint)
-                */
-               /* Make a container so state3 can fill it if we wish.
-                * If that failed we simply do not provide the extended state info.
-                */
-               device_state_info = alloc_device_state_info();
-               state = ast_extension_state3(hint_app, device_state_info);
-               if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
-                       ao2_cleanup(device_state_info);
-                       continue;
-               }
-
-               /* Device state changed since last check - notify the watchers. */
-               hint->laststate = state;        /* record we saw the change */
-
-               /* For general callbacks */
-               cb_iter = ao2_iterator_init(statecbs, 0);
-               for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-                       execute_state_callback(state_cb->change_cb,
-                               context_name,
-                               exten_name,
-                               state_cb->data,
-                               AST_HINT_UPDATE_DEVICE,
-                               hint,
-                               NULL);
-               }
-               ao2_iterator_destroy(&cb_iter);
-
-               /* For extension callbacks */
-               /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
-                * included. Normal callbacks are only called when the state changed.
-                */
-               cb_iter = ao2_iterator_init(hint->callbacks, 0);
-               for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
-                       if (state_cb->extended && first_extended_cb_call) {
-                               /* Fill detailed device_state_info now that we know it is used by extd. callback */
-                               first_extended_cb_call = 0;
-                               get_device_state_causing_channels(device_state_info);
-                       }
-                       if (state_cb->extended || !same_state) {
-                               execute_state_callback(state_cb->change_cb,
-                                       context_name,
-                                       exten_name,
-                                       state_cb->data,
-                                       AST_HINT_UPDATE_DEVICE,
-                                       hint,
-                                       state_cb->extended ? device_state_info : NULL);
-                       }
-               }
-               ao2_iterator_destroy(&cb_iter);
+       publish_hint_change(hint, ne);
 
-               ao2_cleanup(device_state_info);
-       }
-       ast_mutex_unlock(&context_merge_lock);
+       ao2_ref(hint, -1);
 
-       ao2_iterator_destroy(dev_iter);
-       ast_free(hint_app);
-       return;
+       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)
+/*! \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)
 {
-       struct ast_state_cb *state_cb = doomed;
+       struct ast_exten *e = ast_hint_extension(c, context, exten);
 
-       if (state_cb->destroy_cb) {
-               state_cb->destroy_cb(state_cb->id, state_cb->data);
+       if (e) {
+               if (hint)
+                       ast_copy_string(hint, ast_get_extension_app(e), hintsize);
+               if (name) {
+                       const char *tmp = ast_get_extension_app_data(e);
+                       if (tmp)
+                               ast_copy_string(name, tmp, namesize);
+               }
+               return -1;
        }
+       return 0;
 }
 
-/*!
- * \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)
+/*! \brief Get hint for channel */
+int ast_str_get_hint(struct ast_str **hint, ssize_t hintsize, struct ast_str **name, ssize_t namesize, struct ast_channel *c, const char *context, const char *exten)
 {
-       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);
+       struct ast_exten *e = ast_hint_extension(c, context, exten);
 
-               ao2_ref(state_cb, -1);
-               ao2_unlock(statecbs);
+       if (!e) {
                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 (hint) {
+               ast_str_set(hint, hintsize, "%s", ast_get_extension_app(e));
        }
-
-       /* 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;
+       if (name) {
+               const char *tmp = ast_get_extension_app_data(e);
+               if (tmp) {
+                       ast_str_set(name, namesize, "%s", tmp);
                }
        }
+       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);
+int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
+}
 
-       return id;
+int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
+{
+       return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
 }
 
-/*! \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)
+int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
 {
-       return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 0);
+       return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 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)
+int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
 {
-       return extension_state_add_destroy(context, exten, change_cb, NULL, data, 0);
+       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 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)
+int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
 {
-       return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 1);
+       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
 }
 
-/*! \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)
+int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
 {
-       return extension_state_add_destroy(context, exten, change_cb, NULL, data, 1);
+       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
 }
 
-/*! \brief Find Hint by callback id */
-static int find_hint_by_cb_id(void *obj, void *arg, int flags)
+void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
 {
-       struct ast_state_cb *state_cb;
-       const struct ast_hint *hint = obj;
-       int *id = arg;
+       int autoloopflag;
+       int found;
+       int spawn_error;
 
-       if ((state_cb = ao2_find(hint->callbacks, id, 0))) {
-               ao2_ref(state_cb, -1);
-               return CMP_MATCH | CMP_STOP;
-       }
+       ast_channel_lock(chan);
 
-       return 0;
-}
+       /*
+        * 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);
 
-/*! \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;
+       /* Set h exten location */
+       if (context != ast_channel_context(chan)) {
+               ast_channel_context_set(chan, context);
+       }
+       ast_channel_exten_set(chan, "h");
+       ast_channel_priority_set(chan, 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;
+       /* 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);
+       ast_channel_unlock(chan);
 
-               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);
+       for (;;) {
+               spawn_error = ast_spawn_extension(chan, ast_channel_context(chan),
+                       ast_channel_exten(chan), ast_channel_priority(chan),
+                       S_COR(ast_channel_caller(chan)->id.number.valid,
+                               ast_channel_caller(chan)->id.number.str, NULL), &found, 1);
+
+               ast_channel_lock(chan);
+               if (spawn_error) {
+                       /* The code after the loop needs the channel locked. */
+                       break;
                }
-               ao2_unlock(hints);
+               ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
+               ast_channel_unlock(chan);
+       }
+       if (found && spawn_error) {
+               /* Something bad happened, or a hangup has been requested. */
+               ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n",
+                       ast_channel_context(chan), ast_channel_exten(chan),
+                       ast_channel_priority(chan), ast_channel_name(chan));
+               ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
+                       ast_channel_context(chan), ast_channel_exten(chan),
+                       ast_channel_priority(chan), ast_channel_name(chan));
        }
 
-       return ret;
+       /* An "h" exten has been run, so indicate that one has been run. */
+       ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
+
+       /* Restore autoloop flag */
+       ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
+       ast_channel_unlock(chan);
 }
 
-static int hint_id_cmp(void *obj, void *arg, int flags)
+/*! helper function to set extension and priority */
+void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
 {
-       const struct ast_state_cb *cb = obj;
-       int *id = arg;
-
-       return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
+       ast_channel_lock(c);
+       ast_channel_exten_set(c, exten);
+       ast_channel_priority_set(c, pri);
+       ast_channel_unlock(c);
 }
 
 /*!
- * \internal
- * \brief Destroy the given hint object.
- *
- * \param obj Hint to destroy.
- *
- * \return Nothing
- */
-static void destroy_hint(void *obj)
+ * \brief collect digits from the channel into the buffer.
+ * \param c, buf, buflen, pos
+ * \param waittime is in milliseconds
+ * \retval 0 on timeout or done.
+ * \retval -1 on error.
+*/
+static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
 {
-       struct ast_hint *hint = obj;
-
-       if (hint->callbacks) {
-               struct ast_state_cb *state_cb;
-               const char *context_name;
-               const char *exten_name;
+       int digit;
 
-               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;
+       buf[pos] = '\0';        /* make sure it is properly terminated */
+       while (ast_matchmore_extension(c, ast_channel_context(c), buf, 1,
+               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
+               /* As long as we're willing to wait, and as long as it's not defined,
+                  keep reading digits until we can't possibly get a right answer anymore.  */
+               digit = ast_waitfordigit(c, waittime);
+               if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
+                       ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
                } 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);
+                       if (!digit)     /* No entry */
+                               break;
+                       if (digit < 0)  /* Error, maybe a  hangup */
+                               return -1;
+                       if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
+                               buf[pos++] = digit;
+                               buf[pos] = '\0';
+                       }
+                       waittime = ast_channel_pbx(c)->dtimeoutms;
                }
-               ao2_ref(hint->callbacks, -1);
        }
-       ast_free(hint->last_presence_subtype);
-       ast_free(hint->last_presence_message);
+       return 0;
 }
 
-/*! \brief Remove hint from extension */
-static int ast_remove_hint(struct ast_exten *e)
+static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
+               struct ast_pbx_args *args)
 {
-       /* Cleanup the Notifys if hint is removed */
-       struct ast_hint *hint;
+       int found = 0;  /* set if we find at least one match */
+       int res = 0;
+       int autoloopflag;
+       int error = 0;          /* set an error conditions */
+       struct ast_pbx *pbx;
+       ast_callid callid;
 
-       if (!e) {
-               return -1;
+       /* A little initial setup here */
+       if (ast_channel_pbx(c)) {
+               ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c));
+               /* XXX and now what ? */
+               ast_free(ast_channel_pbx(c));
+       }
+       if (!(pbx = ast_calloc(1, sizeof(*pbx)))) {
+               return AST_PBX_FAILED;
        }
 
-       hint = ao2_find(hints, e, OBJ_UNLINK);
-       if (!hint) {
-               return -1;
+       callid = ast_read_threadstorage_callid();
+       /* If the thread isn't already associated with a callid, we should create that association. */
+       if (!callid) {
+               /* Associate new PBX thread with the channel call id if it is availble.
+                * If not, create a new one instead.
+                */
+               callid = ast_channel_callid(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 = 0;
        }
 
-       remove_hintdevice(hint);
+       ast_channel_pbx_set(c, pbx);
+       /* Set reasonable defaults */
+       ast_channel_pbx(c)->rtimeoutms = 10000;
+       ast_channel_pbx(c)->dtimeoutms = 5000;
 
-       /*
-        * The extension is being destroyed so we must save some
-        * information to notify that the extension is deactivated.
-        */
-       ao2_lock(hint);
-       ast_copy_string(hint->context_name,
-               ast_get_context_name(ast_get_extension_context(hint->exten)),
-               sizeof(hint->context_name));
-       ast_copy_string(hint->exten_name, ast_get_extension_name(hint->exten),
-               sizeof(hint->exten_name));
-       hint->exten = NULL;
-       ao2_unlock(hint);
+       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);
 
-       ao2_ref(hint, -1);
+       if (ast_strlen_zero(ast_channel_exten(c))) {
+               /* If not successful fall back to 's' - but only if there is no given exten  */
+               ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
+               /* XXX the original code used the existing priority in the call to
+                * ast_exists_extension(), and reset it to 1 afterwards.
+                * I believe the correct thing is to set it to 1 immediately.
+               */
+               set_ext_pri(c, "s", 1);
+       }
 
-       return 0;
-}
+       for (;;) {
+               char dst_exten[256];    /* buffer to accumulate digits */
+               int pos = 0;            /* XXX should check bounds */
+               int digit = 0;
+               int invalid = 0;
+               int timeout = 0;
 
-/*! \brief Add hint to hint list, check initial extension state */
-static int ast_add_hint(struct ast_exten *e)
-{
-       struct ast_hint *hint_new;
-       struct ast_hint *hint_found;
-       char *message = NULL;
-       char *subtype = NULL;
-       int presence_state;
+               /* No digits pressed yet */
+               dst_exten[pos] = '\0';
 
-       if (!e) {
-               return -1;
-       }
+               /* loop on priorities in this context/exten */
+               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))) {
 
-       /*
-        * We must create the hint we wish to add before determining if
-        * it is already in the hints container to avoid possible
-        * deadlock when getting the current extension state.
-        */
-       hint_new = ao2_alloc(sizeof(*hint_new), destroy_hint);
-       if (!hint_new) {
-               return -1;
-       }
+                       if (!ast_check_hangup(c)) {
+                               ast_channel_priority_set(c, ast_channel_priority(c) + 1);
+                               continue;
+                       }
 
-       /* Initialize new hint. */
-       hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
-       if (!hint_new->callbacks) {
-               ao2_ref(hint_new, -1);
-               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;
-       }
-
-       /* Prevent multiple add hints from adding the same hint at the same time. */
-       ao2_lock(hints);
-
-       /* Search if hint exists, do nothing */
-       hint_found = ao2_find(hints, e, 0);
-       if (hint_found) {
-               ao2_ref(hint_found, -1);
-               ao2_unlock(hints);
-               ao2_ref(hint_new, -1);
-               ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n",
-                       ast_get_extension_name(e), ast_get_extension_app(e));
-               return -1;
-       }
-
-       /* Add new hint to the hints container */
-       ast_debug(2, "HINTS: Adding hint %s: %s\n",
-               ast_get_extension_name(e), ast_get_extension_app(e));
-       ao2_link(hints, hint_new);
-       if (add_hintdevice(hint_new, ast_get_extension_app(e))) {
-               ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
-                       ast_get_extension_name(e),
-                       ast_get_context_name(ast_get_extension_context(e)));
-       }
-
-       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)
-{
-       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
-        * name (which is the hash value) could change.
-        */
-       hint = ao2_find(hints, oe, OBJ_UNLINK);
-       if (!hint) {
-               ao2_unlock(hints);
-               return -1;
-       }
-
-       remove_hintdevice(hint);
-
-       /* 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);
-       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)
-{
-       struct ast_exten *e = ast_hint_extension(c, context, exten);
-
-       if (e) {
-               if (hint)
-                       ast_copy_string(hint, ast_get_extension_app(e), hintsize);
-               if (name) {
-                       const char *tmp = ast_get_extension_app_data(e);
-                       if (tmp)
-                               ast_copy_string(name, tmp, namesize);
-               }
-               return -1;
-       }
-       return 0;
-}
-
-/*! \brief Get hint for channel */
-int ast_str_get_hint(struct ast_str **hint, ssize_t hintsize, struct ast_str **name, ssize_t namesize, struct ast_channel *c, const char *context, const char *exten)
-{
-       struct ast_exten *e = ast_hint_extension(c, context, exten);
-
-       if (!e) {
-               return 0;
-       }
-
-       if (hint) {
-               ast_str_set(hint, hintsize, "%s", ast_get_extension_app(e));
-       }
-       if (name) {
-               const char *tmp = ast_get_extension_app_data(e);
-               if (tmp) {
-                       ast_str_set(name, namesize, "%s", tmp);
-               }
-       }
-       return -1;
-}
-
-int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
-{
-       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
-}
-
-int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
-{
-       return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
-}
-
-int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
-{
-       return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
-}
-
-int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
-{
-       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 0);
-}
-
-int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
-{
-       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
-}
-
-int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
-{
-       return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
-}
-
-void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
-{
-       int autoloopflag;
-       int found;
-       int spawn_error;
-
-       ast_channel_lock(chan);
-
-       /* Set h exten location */
-       if (context != ast_channel_context(chan)) {
-               ast_channel_context_set(chan, 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);
-       ast_channel_unlock(chan);
-
-       for (;;) {
-               spawn_error = ast_spawn_extension(chan, ast_channel_context(chan),
-                       ast_channel_exten(chan), ast_channel_priority(chan),
-                       S_COR(ast_channel_caller(chan)->id.number.valid,
-                               ast_channel_caller(chan)->id.number.str, NULL), &found, 1);
-
-               ast_channel_lock(chan);
-               if (spawn_error) {
-                       /* The code after the loop needs the channel locked. */
-                       break;
-               }
-               ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
-               ast_channel_unlock(chan);
-       }
-       if (found && spawn_error) {
-               /* Something bad happened, or a hangup has been requested. */
-               ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n",
-                       ast_channel_context(chan), ast_channel_exten(chan),
-                       ast_channel_priority(chan), ast_channel_name(chan));
-               ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
-                       ast_channel_context(chan), ast_channel_exten(chan),
-                       ast_channel_priority(chan), ast_channel_name(chan));
-       }
-
-       /* An "h" exten has been run, so indicate that one has been run. */
-       ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
-
-       /* Restore autoloop flag */
-       ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
-       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)
-{
-       ast_channel_lock(c);
-       ast_channel_exten_set(c, exten);
-       ast_channel_priority_set(c, pri);
-       ast_channel_unlock(c);
-}
-
-/*!
- * \brief collect digits from the channel into the buffer.
- * \param c, buf, buflen, pos
- * \param waittime is in milliseconds
- * \retval 0 on timeout or done.
- * \retval -1 on error.
-*/
-static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
-{
-       int digit;
-
-       buf[pos] = '\0';        /* make sure it is properly terminated */
-       while (ast_matchmore_extension(c, ast_channel_context(c), buf, 1,
-               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-               /* As long as we're willing to wait, and as long as it's not defined,
-                  keep reading digits until we can't possibly get a right answer anymore.  */
-               digit = ast_waitfordigit(c, waittime);
-               if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
-                       ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
-               } else {
-                       if (!digit)     /* No entry */
-                               break;
-                       if (digit < 0)  /* Error, maybe a  hangup */
-                               return -1;
-                       if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
-                               buf[pos++] = digit;
-                               buf[pos] = '\0';
-                       }
-                       waittime = ast_channel_pbx(c)->dtimeoutms;
-               }
-       }
-       return 0;
-}
-
-static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
-               struct ast_pbx_args *args)
-{
-       int found = 0;  /* set if we find at least one match */
-       int res = 0;
-       int autoloopflag;
-       int error = 0;          /* set an error conditions */
-       struct ast_pbx *pbx;
-       struct ast_callid *callid;
-
-       /* A little initial setup here */
-       if (ast_channel_pbx(c)) {
-               ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c));
-               /* XXX and now what ? */
-               ast_free(ast_channel_pbx(c));
-       }
-       if (!(pbx = ast_calloc(1, sizeof(*pbx)))) {
-               return AST_PBX_FAILED;
-       }
-
-       callid = ast_read_threadstorage_callid();
-       /* If the thread isn't already associated with a callid, we should create that association. */
-       if (!callid) {
-               /* Associate new PBX thread with the channel call id if it is availble.
-                * If not, create a new one instead.
-                */
-               callid = ast_channel_callid(c);
-               if (!callid) {
-                       callid = ast_create_callid();
-                       if (callid) {
-                               ast_channel_callid_set(c, callid);
-                       }
-               }
-               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);
-       }
-
-       ast_channel_pbx_set(c, pbx);
-       /* Set reasonable defaults */
-       ast_channel_pbx(c)->rtimeoutms = 10000;
-       ast_channel_pbx(c)->dtimeoutms = 5000;
-
-       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);
-
-       if (ast_strlen_zero(ast_channel_exten(c))) {
-               /* If not successful fall back to 's' - but only if there is no given exten  */
-               ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
-               /* XXX the original code used the existing priority in the call to
-                * ast_exists_extension(), and reset it to 1 afterwards.
-                * I believe the correct thing is to set it to 1 immediately.
-               */
-               set_ext_pri(c, "s", 1);
-       }
-
-       for (;;) {
-               char dst_exten[256];    /* buffer to accumulate digits */
-               int pos = 0;            /* XXX should check bounds */
-               int digit = 0;
-               int invalid = 0;
-               int timeout = 0;
-
-               /* No digits pressed yet */
-               dst_exten[pos] = '\0';
-
-               /* loop on priorities in this context/exten */
-               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;
-                       }
-
-                       /* Check softhangup flags. */
-                       if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
-                               ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
-                               continue;
-                       }
-                       if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_TIMEOUT) {
-                               if (ast_exists_extension(c, ast_channel_context(c), "T", 1,
-                                       S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                       set_ext_pri(c, "T", 1);
-                                       /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
-                                       memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
-                                       ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
-                                       continue;
-                               } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
-                                       S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                       raise_exception(c, "ABSOLUTETIMEOUT", 1);
-                                       /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
-                                       memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
-                                       ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
-                                       continue;
-                               }
-
-                               /* Call timed out with no special extension to jump to. */
-                               error = 1;
-                               break;
-                       }
-                       ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
-                               ast_channel_exten(c), ast_channel_priority(c));
-                       error = 1;
-                       break;
-               } /* end while  - from here on we can use 'break' to go out */
-               if (found && res) {
-                       /* Something bad happened, or a hangup has been requested. */
-                       if (strchr("0123456789ABCDEF*#", res)) {
-                               ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
-                               pos = 0;
-                               dst_exten[pos++] = digit = res;
-                               dst_exten[pos] = '\0';
-                       } else if (res == AST_PBX_INCOMPLETE) {
-                               ast_debug(1, "Spawn extension (%s,%s,%d) exited INCOMPLETE on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-                               ast_verb(2, "Spawn extension (%s, %s, %d) exited INCOMPLETE on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-
-                               /* Don't cycle on incomplete - this will happen if the only extension that matches is our "incomplete" extension */
-                               if (!ast_matchmore_extension(c, ast_channel_context(c), ast_channel_exten(c), 1,
-                                       S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                       invalid = 1;
-                               } else {
-                                       ast_copy_string(dst_exten, ast_channel_exten(c), sizeof(dst_exten));
-                                       digit = 1;
-                                       pos = strlen(dst_exten);
-                               }
-                       } else {
-                               ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-                               ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-
-                               if ((res == AST_PBX_ERROR)
-                                       && ast_exists_extension(c, ast_channel_context(c), "e", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                       /* if we are already on the 'e' exten, don't jump to it again */
-                                       if (!strcmp(ast_channel_exten(c), "e")) {
-                                               ast_verb(2, "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-                                               error = 1;
-                                       } else {
-                                               raise_exception(c, "ERROR", 1);
-                                               continue;
-                                       }
-                               }
-
-                               if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
-                                       ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
-                                       continue;
-                               }
-                               if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_TIMEOUT) {
-                                       if (ast_exists_extension(c, ast_channel_context(c), "T", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                               set_ext_pri(c, "T", 1);
-                                               /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
-                                               memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
-                                               ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
-                                               continue;
-                                       } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                               raise_exception(c, "ABSOLUTETIMEOUT", 1);
-                                               /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
-                                               memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
-                                               ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
-                                               continue;
-                                       }
-                                       /* Call timed out with no special extension to jump to. */
-                               }
-                               error = 1;
-                               break;
-                       }
-               }
-               if (error)
-                       break;
-
-               /*!\note
-                * We get here on a failure of some kind:  non-existing extension or
-                * hangup.  We have options, here.  We can either catch the failure
-                * and continue, or we can drop out entirely. */
-
-               if (invalid
-                       || (ast_strlen_zero(dst_exten) &&
-                               !ast_exists_extension(c, ast_channel_context(c), ast_channel_exten(c), 1,
-                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL)))) {
-                       /*!\note
-                        * If there is no match at priority 1, it is not a valid extension anymore.
-                        * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
-                        * neither exist.
-                        */
-                       if (ast_exists_extension(c, ast_channel_context(c), "i", 1,
-                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                               ast_verb(3, "Channel '%s' sent to invalid extension: context,exten,priority=%s,%s,%d\n",
-                                       ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
-                               pbx_builtin_setvar_helper(c, "INVALID_EXTEN", ast_channel_exten(c));
-                               set_ext_pri(c, "i", 1);
-                       } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
-                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                               raise_exception(c, "INVALID", 1);
-                       } else {
-                               ast_log(LOG_WARNING, "Channel '%s' sent to invalid extension but no invalid handler: context,exten,priority=%s,%s,%d\n",
-                                       ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
-                               error = 1; /* we know what to do with it */
-                               break;
-                       }
-               } else if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_TIMEOUT) {
-                       /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
-                       ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
-               } else {        /* keypress received, get more digits for a full extension */
-                       int waittime = 0;
-                       if (digit)
-                               waittime = ast_channel_pbx(c)->dtimeoutms;
-                       else if (!autofallthrough)
-                               waittime = ast_channel_pbx(c)->rtimeoutms;
-                       if (!waittime) {
-                               const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
-                               if (!status)
-                                       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");
-                               else if (!strcasecmp(status, "CHANUNAVAIL"))
-                                       res = pbx_builtin_congestion(c, "10");
-                               else if (!strcasecmp(status, "BUSY"))
-                                       res = pbx_builtin_busy(c, "10");
-                               error = 1; /* XXX disable message */
-                               break;  /* exit from the 'for' loop */
-                       }
-
-                       if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
-                               break;
-                       if (res == AST_PBX_INCOMPLETE && ast_strlen_zero(&dst_exten[pos]))
-                               timeout = 1;
-                       if (!timeout
-                               && ast_exists_extension(c, ast_channel_context(c), dst_exten, 1,
-                                       S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) { /* Prepare the next cycle */
-                               set_ext_pri(c, dst_exten, 1);
-                       } else {
-                               /* No such extension */
-                               if (!timeout && !ast_strlen_zero(dst_exten)) {
-                                       /* An invalid extension */
-                                       if (ast_exists_extension(c, ast_channel_context(c), "i", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                               ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, ast_channel_context(c), ast_channel_name(c));
-                                               pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
-                                               set_ext_pri(c, "i", 1);
-                                       } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                               raise_exception(c, "INVALID", 1);
-                                       } else {
-                                               ast_log(LOG_WARNING,
-                                                       "Invalid extension '%s', but no rule 'i' or 'e' in context '%s'\n",
-                                                       dst_exten, ast_channel_context(c));
-                                               found = 1; /* XXX disable message */
-                                               break;
-                                       }
-                               } else {
-                                       /* A simple timeout */
-                                       if (ast_exists_extension(c, ast_channel_context(c), "t", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                               ast_verb(3, "Timeout on %s\n", ast_channel_name(c));
-                                               set_ext_pri(c, "t", 1);
-                                       } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
-                                               S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
-                                               raise_exception(c, "RESPONSETIMEOUT", 1);
-                                       } else {
-                                               ast_log(LOG_WARNING,
-                                                       "Timeout, but no rule 't' or 'e' in context '%s'\n",
-                                                       ast_channel_context(c));
-                                               found = 1; /* XXX disable message */
-                                               break;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       if (!found && !error) {
-               ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", ast_channel_name(c));
-       }
-
-       if (!args || !args->no_hangup_chan) {
-               ast_softhangup(c, AST_SOFTHANGUP_APPUNLOAD);
-               if (!ast_test_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN)
-                       && ast_exists_extension(c, ast_channel_context(c), "h", 1,
-                               S_COR(ast_channel_caller(c)->id.number.valid,
-                                       ast_channel_caller(c)->id.number.str, NULL))) {
-                       ast_pbx_h_exten_run(c, ast_channel_context(c));
-               }
-               ast_pbx_hangup_handler_run(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 */
-       pbx_destroy(ast_channel_pbx(c));
-       ast_channel_pbx_set(c, NULL);
-
-       if (!args || !args->no_hangup_chan) {
-               ast_hangup(c);
-       }
-
-       return AST_PBX_SUCCESS;
-}
-
-/*!
- * \brief Increase call count for channel
- * \retval 0 on success
- * \retval non-zero if a configured limit (maxcalls, maxload, minmemfree) was reached
-*/
-static int increase_call_count(const struct ast_channel *c)
-{
-       int failed = 0;
-       double curloadavg;
-#if defined(HAVE_SYSINFO)
-       long curfreemem;
-       struct sysinfo sys_info;
-#endif
-
-       ast_mutex_lock(&maxcalllock);
-       if (ast_option_maxcalls) {
-               if (countcalls >= ast_option_maxcalls) {
-                       ast_log(LOG_WARNING, "Maximum call limit of %d calls exceeded by '%s'!\n", ast_option_maxcalls, ast_channel_name(c));
-                       failed = -1;
-               }
-       }
-       if (ast_option_maxload) {
-               getloadavg(&curloadavg, 1);
-               if (curloadavg >= ast_option_maxload) {
-                       ast_log(LOG_WARNING, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", ast_option_maxload, ast_channel_name(c), curloadavg);
-                       failed = -1;
-               }
-       }
-#if defined(HAVE_SYSINFO)
-       if (option_minmemfree) {
-               if (!sysinfo(&sys_info)) {
-                       /* make sure that the free system memory is above the configured low watermark
-                        * convert the amount of freeram from mem_units to MB */
-                       curfreemem = sys_info.freeram * sys_info.mem_unit;
-                       curfreemem /= 1024 * 1024;
-                       if (curfreemem < option_minmemfree) {
-                               ast_log(LOG_WARNING, "Available system memory (~%ldMB) is below the configured low watermark (%ldMB)\n", curfreemem, option_minmemfree);
-                               failed = -1;
-                       }
-               }
-       }
-#endif
-
-       if (!failed) {
-               countcalls++;
-               totalcalls++;
-       }
-       ast_mutex_unlock(&maxcalllock);
-
-       return failed;
-}
-
-static void decrease_call_count(void)
-{
-       ast_mutex_lock(&maxcalllock);
-       if (countcalls > 0)
-               countcalls--;
-       ast_mutex_unlock(&maxcalllock);
-}
-
-static void destroy_exten(struct ast_exten *e)
-{
-       if (e->priority == PRIORITY_HINT)
-               ast_remove_hint(e);
-
-       if (e->peer_table)
-               ast_hashtab_destroy(e->peer_table,0);
-       if (e->peer_label_table)
-               ast_hashtab_destroy(e->peer_label_table, 0);
-       if (e->datad)
-               e->datad(e->data);
-       ast_free(e);
-}
-
-static void *pbx_thread(void *data)
-{
-       /* Oh joyeous kernel, we're a new thread, with nothing to do but
-          answer this channel and get it going.
-       */
-       /* NOTE:
-          The launcher of this function _MUST_ increment 'countcalls'
-          before invoking the function; it will be decremented when the
-          PBX has finished running on the channel
-        */
-       struct ast_channel *c = data;
-
-       __ast_pbx_run(c, NULL);
-       decrease_call_count();
-
-       pthread_exit(NULL);
-
-       return NULL;
-}
-
-enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
-{
-       pthread_t t;
-
-       if (!c) {
-               ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
-               return AST_PBX_FAILED;
-       }
-
-       if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
-               ast_log(LOG_WARNING, "PBX requires Asterisk to be fully booted\n");
-               return AST_PBX_FAILED;
-       }
-
-       if (increase_call_count(c))
-               return AST_PBX_CALL_LIMIT;
-
-       /* Start a new thread, and get something handling this channel. */
-       if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
-               ast_log(LOG_WARNING, "Failed to create new channel thread\n");
-               decrease_call_count();
-               return AST_PBX_FAILED;
-       }
-
-       return AST_PBX_SUCCESS;
-}
-
-enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
-{
-       enum ast_pbx_result res = AST_PBX_SUCCESS;
-
-       if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
-               ast_log(LOG_WARNING, "PBX requires Asterisk to be fully booted\n");
-               return AST_PBX_FAILED;
-       }
-
-       if (increase_call_count(c)) {
-               return AST_PBX_CALL_LIMIT;
-       }
-
-       res = __ast_pbx_run(c, args);
-
-       decrease_call_count();
-
-       return res;
-}
-
-enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
-{
-       return ast_pbx_run_args(c, NULL);
-}
-
-int ast_active_calls(void)
-{
-       return countcalls;
-}
-
-int ast_processed_calls(void)
-{
-       return totalcalls;
-}
-
-int pbx_set_autofallthrough(int newval)
-{
-       int oldval = autofallthrough;
-       autofallthrough = newval;
-       return oldval;
-}
-
-int pbx_set_extenpatternmatchnew(int newval)
-{
-       int oldval = extenpatternmatchnew;
-       extenpatternmatchnew = newval;
-       return oldval;
-}
-
-void pbx_set_overrideswitch(const char *newval)
-{
-       if (overrideswitch) {
-               ast_free(overrideswitch);
-       }
-       if (!ast_strlen_zero(newval)) {
-               overrideswitch = ast_strdup(newval);
-       } else {
-               overrideswitch = NULL;
-       }
-}
-
-/*!
- * \brief lookup for a context with a given name,
- * \retval found context or NULL if not found.
- */
-static struct ast_context *find_context(const char *context)
-{
-       struct fake_context item;
-
-       ast_copy_string(item.name, context, sizeof(item.name));
-
-       return ast_hashtab_lookup(contexts_table, &item);
-}
-
-/*!
- * \brief lookup for a context with a given name,
- * \retval with conlock held if found.
- * \retval NULL if not found.
- */
-static struct ast_context *find_context_locked(const char *context)
-{
-       struct ast_context *c;
-       struct fake_context item;
-
-       ast_copy_string(item.name, context, sizeof(item.name));
-
-       ast_rdlock_contexts();
-       c = ast_hashtab_lookup(contexts_table, &item);
-       if (!c) {
-               ast_unlock_contexts();
-       }
-
-       return c;
-}
-
-/*!
- * \brief Remove included contexts.
- * This function locks contexts list by &conlist, search for the right context
- * structure, leave context list locked and call ast_context_remove_include2
- * which removes include, unlock contexts list and return ...
- */
-int ast_context_remove_include(const char *context, const char *include, const char *registrar)
-{
-       int ret = -1;
-       struct ast_context *c;
-
-       c = find_context_locked(context);
-       if (c) {
-               /* found, remove include from this context ... */
-               ret = ast_context_remove_include2(c, include, registrar);
-               ast_unlock_contexts();
-       }
-       return ret;
-}
-
-/*!
- * \brief Locks context, remove included contexts, unlocks context.
- * When we call this function, &conlock lock must be locked, because when
- * we giving *con argument, some process can remove/change this context
- * and after that there can be segfault.
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
-{
-       struct ast_include *i, *pi = NULL;
-       int ret = -1;
-
-       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))) {
-                       /* 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;
-                       /* free include and return */
-                       ast_destroy_timing(&(i->timing));
-                       ast_free(i);
-                       ret = 0;
-                       break;
-               }
-       }
-
-       ast_unlock_context(con);
-
-       return ret;
-}
-
-/*!
- * \note This function locks contexts list by &conlist, search for the rigt context
- * structure, leave context list locked and call ast_context_remove_switch2
- * which removes switch, unlock contexts list and return ...
- */
-int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
-{
-       int ret = -1; /* default error return */
-       struct ast_context *c;
-
-       c = find_context_locked(context);
-       if (c) {
-               /* remove switch from this context ... */
-               ret = ast_context_remove_switch2(c, sw, data, registrar);
-               ast_unlock_contexts();
-       }
-       return ret;
-}
-
-/*!
- * \brief This function locks given context, removes switch, unlock context and
- * return.
- * \note When we call this function, &conlock lock must be locked, because when
- * we giving *con argument, some process can remove/change this context
- * and after that there can be segfault.
- *
- */
-int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
-{
-       struct ast_sw *i;
-       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))) {
-                       /* 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 */
-                       ret = 0;
-                       break;
-               }
-       }
-       AST_LIST_TRAVERSE_SAFE_END;
-
-       ast_unlock_context(con);
-
-       return ret;
-}
-
-/*! \note This function will lock conlock. */
-int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
-{
-       return ast_context_remove_extension_callerid(context, extension, priority, NULL, 0, registrar);
-}
-
-int ast_context_remove_extension_callerid(const char *context, const char *extension, int priority, const char *callerid, int matchcallerid, const char *registrar)
-{
-       int ret = -1; /* default error return */
-       struct ast_context *c;
-
-       c = find_context_locked(context);
-       if (c) { /* ... remove extension ... */
-               ret = ast_context_remove_extension_callerid2(c, extension, priority, callerid,
-                       matchcallerid, registrar, 0);
-               ast_unlock_contexts();
-       }
-
-       return ret;
-}
-
-/*!
- * \brief This functionc locks given context, search for the right extension and
- * fires out all peer in this extensions with given priority. If priority
- * is set to 0, all peers are removed. After that, unlock context and
- * return.
- * \note When do you want to call this function, make sure that &conlock is locked,
- * because some process can handle with your *con context before you lock
- * it.
- *
- */
-int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar, int already_locked)
-{
-       return ast_context_remove_extension_callerid2(con, extension, priority, NULL, 0, registrar, already_locked);
-}
-
-int ast_context_remove_extension_callerid2(struct ast_context *con, const char *extension, int priority, const char *callerid, int matchcallerid, const char *registrar, int already_locked)
-{
-       struct ast_exten *exten, *prev_exten = NULL;
-       struct ast_exten *peer;
-       struct ast_exten ex, *exten2, *exten3;
-       char dummy_name[1024];
-       struct ast_exten *previous_peer = NULL;
-       struct ast_exten *next_peer = NULL;
-       int found = 0;
-
-       if (!already_locked)
-               ast_wrlock_context(con);
-
-       /* Handle this is in the new world */
-
-       /* FIXME For backwards compatibility, if callerid==NULL, then remove ALL
-        * peers, not just those matching the callerid. */
-#ifdef NEED_DEBUG
-       ast_verb(3,"Removing %s/%s/%d%s%s from trees, registrar=%s\n", con->name, extension, priority, matchcallerid ? "/" : "", matchcallerid ? callerid : "", registrar);
-#endif
-#ifdef CONTEXT_DEBUG
-       check_contexts(__FILE__, __LINE__);
-#endif
-       /* find this particular extension */
-       ex.exten = dummy_name;
-       ex.matchcid = matchcallerid && !ast_strlen_zero(callerid); /* don't say match if there's no callerid */
-       ex.cidmatch = callerid;
-       ast_copy_string(dummy_name, extension, sizeof(dummy_name));
-       exten = ast_hashtab_lookup(con->root_table, &ex);
-       if (exten) {
-               if (priority == 0) {
-                       exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
-                       if (!exten2)
-                               ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_table\n", extension, con->name);
-                       if (con->pattern_tree) {
-                               struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
-
-                               if (x->exten) { /* this test for safety purposes */
-                                       x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
-                                       x->exten = 0; /* get rid of what will become a bad pointer */
-                               } else {
-                                       ast_log(LOG_WARNING,"Trying to delete an exten from a context, but the pattern tree node returned isn't a full extension\n");
-                               }
-                       }
-               } else {
-                       ex.priority = priority;
-                       exten2 = ast_hashtab_lookup(exten->peer_table, &ex);
-                       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);
-                               }
-
-                               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 (exten2 == exten && exten2->peer) {
-                                       exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
-                                       ast_hashtab_insert_immediate(con->root_table, exten2->peer);
-                               }
-                               if (ast_hashtab_size(exten->peer_table) == 0) {
-                                       /* 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 (con->pattern_tree) {
-                                               struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
-                                               if (x->exten) { /* this test for safety purposes */
-                                                       x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
-                                                       x->exten = 0; /* get rid of what will become a bad pointer */
-                                               }
-                                       }
-                               }
-                       } else {
-                               ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
-                                               priority, exten->exten, con->name);
-                       }
-               }
-       } else {
-               /* hmmm? this exten is not in this pattern tree? */
-               ast_log(LOG_WARNING,"Cannot find extension %s in root_table in context %s\n",
-                               extension, con->name);
-       }
-#ifdef NEED_DEBUG
-       if (con->pattern_tree) {
-               ast_log(LOG_NOTICE,"match char tree after exten removal:\n");
-               log_match_char_tree(con->pattern_tree, " ");
-       }
-#endif
-
-       /* 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))))
-                       break;
-       }
-       if (!exten) {
-               /* we can't find right extension */
-               if (!already_locked)
-                       ast_unlock_context(con);
-               return -1;
-       }
-
-       /* 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) && (!matchcallerid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(peer->cidmatch) && !strcmp(peer->cidmatch,callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(peer->cidmatch)));
-                       peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
-               if ((priority == 0 || peer->priority == priority) &&
-                               (!callerid || !matchcallerid || (matchcallerid && !strcmp(peer->cidmatch, callerid))) &&
-                               (!registrar || !strcmp(peer->registrar, registrar) )) {
-                       found = 1;
-
-                       /* we are first priority extension? */
-                       if (!previous_peer) {
-                               /*
-                                * We are first in the priority chain, so must update the extension chain.
-                                * The next node is either the next priority or the next extension
-                                */
-                               struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
-                               if (peer->peer) {
-                                       /* move the peer_table and peer_label_table down to the next peer, if
-                                          it is there */
-                                       peer->peer->peer_table = peer->peer_table;
-                                       peer->peer->peer_label_table = peer->peer_label_table;
-                                       peer->peer_table = NULL;
-                                       peer->peer_label_table = NULL;
-                               }
-                               if (!prev_exten) {      /* change the root... */
-                                       con->root = next_node;
-                               } else {
-                                       prev_exten->next = next_node; /* unlink */
-                               }
-                               if (peer->peer) { /* update the new head of the pri list */
-                                       peer->peer->next = peer->next;
-                               }
-                       } else { /* easy, we are not first priority in extension */
-                               previous_peer->peer = peer->peer;
-                       }
-
-                       /* now, free whole priority extension */
-                       destroy_exten(peer);
-               } else {
-                       previous_peer = peer;
-               }
-       }
-       if (!already_locked)
-               ast_unlock_context(con);
-       return found ? 0 : -1;
-}
-
-
-/*!
- * \note This function locks contexts list by &conlist, searches for the right context
- * structure, and locks the macrolock mutex in that context.
- * macrolock is used to limit a macro to be executed by one call at a time.
- * \param context The context
- */
-int ast_context_lockmacro(const char *context)
-{
-       struct ast_context *c;
-       int ret = -1;
-
-       c = find_context_locked(context);
-       if (c) {
-               ast_unlock_contexts();
-
-               /* if we found context, lock macrolock */
-               ret = ast_mutex_lock(&c->macrolock);
-       }
-
-       return ret;
-}
-
-/*!
- * \note This function locks contexts list by &conlist, searches for the right context
- * structure, and unlocks the macrolock mutex in that context.
- * macrolock is used to limit a macro to be executed by one call at a time.
- * \param context The context
- */
-int ast_context_unlockmacro(const char *context)
-{
-       struct ast_context *c;
-       int ret = -1;
-
-       c = find_context_locked(context);
-       if (c) {
-               ast_unlock_contexts();
-
-               /* if we found context, unlock macrolock */
-               ret = ast_mutex_unlock(&c->macrolock);
-       }
-
-       return ret;
-}
-
-/*! \brief Dynamically register a new dial plan application */
-int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, const char *), const char *synopsis, const char *description, void *mod)
-{
-       struct ast_app *tmp;
-       struct ast_app *cur;
-       int length;
-#ifdef AST_XML_DOCS
-       char *tmpxml;
-#endif
-
-       AST_RWLIST_WRLOCK(&apps);
-       cur = pbx_findapp_nolock(app);
-       if (cur) {
-               ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
-       }
-
-       length = sizeof(*tmp) + strlen(app) + 1;
-
-       if (!(tmp = ast_calloc(1, length))) {
-               AST_RWLIST_UNLOCK(&apps);
-               return -1;
-       }
-
-       if (ast_string_field_init(tmp, 128)) {
-               AST_RWLIST_UNLOCK(&apps);
-               ast_free(tmp);
-               return -1;
-       }
-
-       strcpy(tmp->name, app);
-       tmp->execute = execute;
-       tmp->module = mod;
-
-#ifdef AST_XML_DOCS
-       /* Try to lookup the docs in our XML documentation database */
-       if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
-               /* load synopsis */
-               tmpxml = ast_xmldoc_build_synopsis("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, synopsis, tmpxml);
-               ast_free(tmpxml);
-
-               /* load description */
-               tmpxml = ast_xmldoc_build_description("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, description, tmpxml);
-               ast_free(tmpxml);
-
-               /* load syntax */
-               tmpxml = ast_xmldoc_build_syntax("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, syntax, tmpxml);
-               ast_free(tmpxml);
-
-               /* load arguments */
-               tmpxml = ast_xmldoc_build_arguments("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, arguments, tmpxml);
-               ast_free(tmpxml);
-
-               /* load seealso */
-               tmpxml = ast_xmldoc_build_seealso("application", app, ast_module_name(tmp->module));
-               ast_string_field_set(tmp, seealso, tmpxml);
-               ast_free(tmpxml);
-               tmp->docsrc = AST_XML_DOC;
-       } else {
-#endif
-               ast_string_field_set(tmp, synopsis, synopsis);
-               ast_string_field_set(tmp, description, description);
-#ifdef AST_XML_DOCS
-               tmp->docsrc = AST_STATIC_DOC;
-       }
-#endif
-
-       /* Store in alphabetical order */
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
-               if (strcasecmp(tmp->name, cur->name) < 0) {
-                       AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
-                       break;
-               }
-       }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       if (!cur)
-               AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
-
-       ast_verb(2, "Registered application '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, tmp->name));
-
-       AST_RWLIST_UNLOCK(&apps);
-
-       return 0;
-}
-
-/*
- * Append to the list. We don't have a tail pointer because we need
- * to scan the list anyways to check for duplicates during insertion.
- */
-int ast_register_switch(struct ast_switch *sw)
-{
-       struct ast_switch *tmp;
-
-       AST_RWLIST_WRLOCK(&switches);
-       AST_RWLIST_TRAVERSE(&switches, tmp, list) {
-               if (!strcasecmp(tmp->name, sw->name)) {
-                       AST_RWLIST_UNLOCK(&switches);
-                       ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
-                       return -1;
-               }
-       }
-       AST_RWLIST_INSERT_TAIL(&switches, sw, list);
-       AST_RWLIST_UNLOCK(&switches);
-
-       return 0;
-}
-
-void ast_unregister_switch(struct ast_switch *sw)
-{
-       AST_RWLIST_WRLOCK(&switches);
-       AST_RWLIST_REMOVE(&switches, sw, list);
-       AST_RWLIST_UNLOCK(&switches);
-}
-
-/*
- * Help for CLI commands ...
- */
-
-static void print_app_docs(struct ast_app *aa, int fd)
-{
-#ifdef AST_XML_DOCS
-       char *synopsis = NULL, *description = NULL, *arguments = NULL, *seealso = NULL;
-       if (aa->docsrc == AST_XML_DOC) {
-               synopsis = ast_xmldoc_printable(S_OR(aa->synopsis, "Not available"), 1);
-               description = ast_xmldoc_printable(S_OR(aa->description, "Not available"), 1);
-               arguments = ast_xmldoc_printable(S_OR(aa->arguments, "Not available"), 1);
-               seealso = ast_xmldoc_printable(S_OR(aa->seealso, "Not available"), 1);
-               if (!synopsis || !description || !arguments || !seealso) {
-                       goto free_docs;
-               }
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s%s%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                               ast_term_color(COLOR_CYAN, 0), S_OR(aa->syntax, "Not available"), ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso);
-free_docs:
-               ast_free(synopsis);
-               ast_free(description);
-               ast_free(arguments);
-               ast_free(seealso);
-       } else
-#endif
-       {
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->synopsis, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->description, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->syntax, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->arguments, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->seealso, "Not available")));
-       }
-}
-
-/*
- * \brief 'show application' CLI command implementation function...
- */
-static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_app *aa;
-       int app, no_registered_app = 1;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "core show application";
-               e->usage =
-                       "Usage: core show application <application> [<application> [<application> [...]]]\n"
-                       "       Describes a particular application.\n";
-               return NULL;
-       case CLI_GENERATE:
-               /*
-                * There is a possibility to show informations about more than one
-                * application at one time. You can type 'show application Dial Echo' and
-                * you will see informations about these two applications ...
-                */
-               return ast_complete_applications(a->line, a->word, a->n);
-       }
-
-       if (a->argc < 4) {
-               return CLI_SHOWUSAGE;
-       }
-
-       AST_RWLIST_RDLOCK(&apps);
-       AST_RWLIST_TRAVERSE(&apps, aa, list) {
-               /* Check for each app that was supplied as an argument */
-               for (app = 3; app < a->argc; app++) {
-                       if (strcasecmp(aa->name, a->argv[app])) {
-                               continue;
-                       }
-
-                       /* We found it! */
-                       no_registered_app = 0;
-
-                       print_app_docs(aa, a->fd);
-               }
-       }
-       AST_RWLIST_UNLOCK(&apps);
-
-       /* we found at least one app? no? */
-       if (no_registered_app) {
-               ast_cli(a->fd, "Your application(s) is (are) not registered\n");
-               return CLI_FAILURE;
-       }
-
-       return CLI_SUCCESS;
-}
-
-/*! \brief  handle_show_hints: CLI support for listing registered dial plan hints */
-static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct ast_hint *hint;
-       int num = 0;
-       int&