loader: Add dependency fields to module structures.
[asterisk/asterisk.git] / res / res_agi.c
index 0a20bbd..2d0dc27 100644 (file)
  *
  * \author Mark Spencer <markster@digium.com>
  *
- * \todo Convert the rest of the AGI commands over to XML documentation
  */
 
 /*** MODULEINFO
+       <depend>res_speech</depend>
        <support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include <math.h>
 #include <signal.h>
 #include <sys/time.h>
@@ -66,6 +64,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/srv.h"
 #include "asterisk/test.h"
 #include "asterisk/netsock2.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/stasis_message_router.h"
+#include "asterisk/format_cache.h"
 
 #define AST_API_MODULE
 #include "asterisk/agi.h"
@@ -82,6 +83,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="agi">hangup</ref>
+                       <ref type="application">AGI</ref>
                </see-also>
        </agi>
        <agi name="asyncagi break" language="en_US">
@@ -95,6 +97,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="agi">hangup</ref>
+                       <ref type="application">AGI</ref>
                </see-also>
        </agi>
        <agi name="channel status" language="en_US">
@@ -135,6 +138,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                </enum>
                        </enumlist>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="control stream file" language="en_US">
                <synopsis>
@@ -147,19 +153,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <parameter name="escape_digits" required="true" />
                        <parameter name="skipms" />
                        <parameter name="ffchar">
-                               <para>Defaults to <literal>*</literal></para>
+                               <para>Defaults to <literal>#</literal></para>
                        </parameter>
                        <parameter name="rewchr">
-                               <para>Defaults to <literal>#</literal></para>
+                               <para>Defaults to <literal>*</literal></para>
                        </parameter>
                        <parameter name="pausechr" />
+                       <parameter name="offsetms">
+                               <para>Offset, in milliseconds, to start the audio playback</para>
+                       </parameter>
                </syntax>
                <description>
                        <para>Send the given file, allowing playback to be controlled by the given
                        digits, if any. Use double quotes for the digits if you wish none to be
-                       permitted. Returns <literal>0</literal> if playback completes without a digit
+                       permitted. If offsetms is provided then the audio will seek to offsetms
+                       before play starts. Returns <literal>0</literal> if playback completes without a digit
                        being pressed, or the ASCII numerical value of the digit if one was pressed,
-                       or <literal>-1</literal> on error or if the channel was disconnected.</para>
+                       or <literal>-1</literal> on error or if the channel was disconnected. Returns the
+                       position where playback was terminated as endpos.</para>
+
                        <para>It sets the following channel variables upon completion:</para>
                        <variablelist>
                                <variable name="CPLAYBACKSTATUS">
@@ -179,6 +191,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                </variable>
                        </variablelist>
                </description>
+               <see-also>
+                       <ref type="agi">get option</ref>
+                       <ref type="agi">control stream file</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="database del" language="en_US">
                <synopsis>
@@ -194,6 +211,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Returns <literal>1</literal> if successful, <literal>0</literal>
                        otherwise.</para>
                </description>
+               <see-also>
+                       <ref type="agi">database get</ref>
+                       <ref type="agi">database put</ref>
+                       <ref type="agi">database deltree</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="database deltree" language="en_US">
                <synopsis>
@@ -208,6 +231,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        within a <replaceable>family</replaceable> in the Asterisk database.</para>
                        <para>Returns <literal>1</literal> if successful, <literal>0</literal> otherwise.</para>
                </description>
+               <see-also>
+                       <ref type="agi">database get</ref>
+                       <ref type="agi">database put</ref>
+                       <ref type="agi">database del</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="database get" language="en_US">
                <synopsis>
@@ -225,6 +254,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        in parenthesis.</para>
                        <para>Example return code: 200 result=1 (testvariable)</para>
                </description>
+               <see-also>
+                       <ref type="agi">database put</ref>
+                       <ref type="agi">database del</ref>
+                       <ref type="agi">database deltree</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="database put" language="en_US">
                <synopsis>
@@ -241,6 +276,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <replaceable>value</replaceable>.</para>
                        <para>Returns <literal>1</literal> if successful, <literal>0</literal> otherwise.</para>
                </description>
+               <see-also>
+                       <ref type="agi">database get</ref>
+                       <ref type="agi">database del</ref>
+                       <ref type="agi">database deltree</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="exec" language="en_US">
                <synopsis>
@@ -256,6 +297,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Returns whatever the <replaceable>application</replaceable> returns, or
                        <literal>-2</literal> on failure to find <replaceable>application</replaceable>.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="get data" language="en_US">
                <synopsis>
@@ -270,6 +314,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Stream the given <replaceable>file</replaceable>, and receive DTMF data.</para>
                        <para>Returns the digits received from the channel at the other end.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="get full variable" language="en_US">
                <synopsis>
@@ -286,6 +333,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        variables, unlike GET VARIABLE.</para>
                        <para>Example return code: 200 result=1 (testvariable)</para>
                </description>
+               <see-also>
+                       <ref type="agi">get variable</ref>
+                       <ref type="agi">set variable</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="get option" language="en_US">
                <synopsis>
@@ -301,6 +353,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="agi">stream file</ref>
+                       <ref type="agi">control stream file</ref>
+                       <ref type="application">AGI</ref>
                </see-also>
        </agi>
        <agi name="get variable" language="en_US">
@@ -316,6 +370,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        the variable in parentheses.</para>
                        <para>Example return code: 200 result=1 (testvariable)</para>
                </description>
+               <see-also>
+                       <ref type="agi">get full variable</ref>
+                       <ref type="agi">set variable</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="hangup" language="en_US">
                <synopsis>
@@ -328,6 +387,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Hangs up the specified channel. If no channel name is given, hangs
                        up the current channel</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="noop" language="en_US">
                <synopsis>
@@ -337,6 +399,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Does nothing.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="receive char" language="en_US">
                <synopsis>
@@ -354,6 +419,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        if one is received, or <literal>0</literal> if the channel does not support
                        text reception. Returns <literal>-1</literal> only on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">receive text</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="receive text" language="en_US">
                <synopsis>
@@ -366,23 +435,49 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        </parameter>
                </syntax>
                <description>
-                       <para>Receives a string of text on a channel. Most channels 
+                       <para>Receives a string of text on a channel. Most channels
                        do not support the reception of text. Returns <literal>-1</literal> for failure
-                       or <literal>1</literal> for success, and the string in parenthesis.</para> 
+                       or <literal>1</literal> for success, and the string in parenthesis.</para>
                </description>
+               <see-also>
+                       <ref type="agi">receive char</ref>
+                       <ref type="agi">send text</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="record file" language="en_US">
                <synopsis>
                        Records to a given file.
                </synopsis>
                <syntax>
-                       <parameter name="filename" required="true" />
-                       <parameter name="format" required="true" />
-                       <parameter name="escape_digits" required="true" />
-                       <parameter name="timeout" required="true" />
-                       <parameter name="offset samples" />
-                       <parameter name="BEEP" />
-                       <parameter name="s=silence" />
+                       <parameter name="filename" required="true">
+                               <para>The destination filename of the recorded audio.</para>
+                       </parameter>
+                       <parameter name="format" required="true">
+                               <para>The audio format in which to save the resulting file.</para>
+                       </parameter>
+                       <parameter name="escape_digits" required="true">
+                               <para>The DTMF digits that will terminate the recording process.</para>
+                       </parameter>
+                       <parameter name="timeout" required="true">
+                               <para>The maximum recording time in milliseconds. Set to -1 for no
+                               limit.</para>
+                       </parameter>
+                       <parameter name="offset_samples">
+                               <para>Causes the recording to first seek to the specified offset before
+                               recording begins.</para>
+                       </parameter>
+                       <parameter name="beep">
+                               <para>Causes Asterisk to play a beep as recording begins. This argument
+                               can take any value.</para>
+                       </parameter>
+                       <parameter name="s=silence">
+                               <para>The number of seconds of silence that are permitted before the
+                               recording is terminated, regardless of the
+                               <replaceable>escape_digits</replaceable> or <replaceable>timeout</replaceable>
+                               arguments. If specified, this parameter must be preceded by
+                               <literal>s=</literal>.</para>
+                       </parameter>
                </syntax>
                <description>
                        <para>Record to a file until a given dtmf digit in the sequence is received.
@@ -390,11 +485,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        will be recorded. The <replaceable>timeout</replaceable> is the maximum record time in
                        milliseconds, or <literal>-1</literal> for no <replaceable>timeout</replaceable>.
                        <replaceable>offset samples</replaceable> is optional, and, if provided, will seek
-                       to the offset without exceeding the end of the file. <replaceable>silence</replaceable> is
+                       to the offset without exceeding the end of the
+                       file. <replaceable>beep</replaceable> can take any value, and causes Asterisk
+                       to play a beep to the channel that is about to be recorded. <replaceable>silence</replaceable> is
                        the number of seconds of silence allowed before the function returns despite the
                        lack of dtmf digits or reaching <replaceable>timeout</replaceable>. <replaceable>silence</replaceable>
                        value must be preceded by <literal>s=</literal> and is also optional.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say alpha" language="en_US">
                <synopsis>
@@ -410,6 +510,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        without a digit being pressed, or the ASCII numerical value of the digit if one
                        was pressed or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say digits</ref>
+                       <ref type="agi">say number</ref>
+                       <ref type="agi">say phonetic</ref>
+                       <ref type="agi">say date</ref>
+                       <ref type="agi">say time</ref>
+                       <ref type="agi">say datetime</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say digits" language="en_US">
                <synopsis>
@@ -425,6 +534,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        without a digit being pressed, or the ASCII numerical value of the digit if one
                        was pressed or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say alpha</ref>
+                       <ref type="agi">say number</ref>
+                       <ref type="agi">say phonetic</ref>
+                       <ref type="agi">say date</ref>
+                       <ref type="agi">say time</ref>
+                       <ref type="agi">say datetime</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say number" language="en_US">
                <synopsis>
@@ -441,6 +559,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        completes without a digit being pressed, or the ASCII numerical value of
                        the digit if one was pressed or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say alpha</ref>
+                       <ref type="agi">say digits</ref>
+                       <ref type="agi">say phonetic</ref>
+                       <ref type="agi">say date</ref>
+                       <ref type="agi">say time</ref>
+                       <ref type="agi">say datetime</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say phonetic" language="en_US">
                <synopsis>
@@ -456,6 +583,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        playback completes without a digit pressed, the ASCII numerical value of the digit
                        if one was pressed, or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say alpha</ref>
+                       <ref type="agi">say digits</ref>
+                       <ref type="agi">say number</ref>
+                       <ref type="agi">say date</ref>
+                       <ref type="agi">say time</ref>
+                       <ref type="agi">say datetime</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say date" language="en_US">
                <synopsis>
@@ -474,6 +610,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        completes without a digit being pressed, or the ASCII numerical value of the
                        digit if one was pressed or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say alpha</ref>
+                       <ref type="agi">say digits</ref>
+                       <ref type="agi">say number</ref>
+                       <ref type="agi">say phonetic</ref>
+                       <ref type="agi">say time</ref>
+                       <ref type="agi">say datetime</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say time" language="en_US">
                <synopsis>
@@ -492,6 +637,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        without a digit being pressed, or the ASCII numerical value of the digit if
                        one was pressed or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say alpha</ref>
+                       <ref type="agi">say digits</ref>
+                       <ref type="agi">say number</ref>
+                       <ref type="agi">say phonetic</ref>
+                       <ref type="agi">say date</ref>
+                       <ref type="agi">say datetime</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="say datetime" language="en_US">
                <synopsis>
@@ -519,6 +673,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        completes without a digit being pressed, or the ASCII numerical value of the
                        digit if one was pressed or <literal>-1</literal> on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">say alpha</ref>
+                       <ref type="agi">say digits</ref>
+                       <ref type="agi">say number</ref>
+                       <ref type="agi">say phonetic</ref>
+                       <ref type="agi">say date</ref>
+                       <ref type="agi">say time</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="send image" language="en_US">
                <synopsis>
@@ -533,6 +696,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        the channel does not support image transmission.  Returns <literal>-1</literal>
                        only on error/hangup. Image names should not include extensions.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="send text" language="en_US">
                <synopsis>
@@ -550,6 +716,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        channel does not support text transmission. Returns <literal>-1</literal> only
                        on error/hangup.</para>
                </description>
+               <see-also>
+                       <ref type="agi">receive text</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set autohangup" language="en_US">
                <synopsis>
@@ -563,6 +733,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        seconds in the future. Of course it can be hungup before then as well. Setting to
                        <literal>0</literal> will cause the autohangup feature to be disabled on this channel.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set callerid" language="en_US">
                <synopsis>
@@ -574,6 +747,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Changes the callerid of the current channel.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set context" language="en_US">
                <synopsis>
@@ -585,6 +761,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Sets the context for continuation upon exiting the application.</para>
                </description>
+               <see-also>
+                       <ref type="agi">set extension</ref>
+                       <ref type="agi">set priority</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set extension" language="en_US">
                <synopsis>
@@ -596,6 +777,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Changes the extension for continuation upon exiting the application.</para>
                </description>
+               <see-also>
+                       <ref type="agi">set context</ref>
+                       <ref type="agi">set priority</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set music" language="en_US">
                <synopsis>
@@ -620,6 +806,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        used. This generator will be stopped automatically when playing a file.</para>
                        <para>Always returns <literal>0</literal>.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set priority" language="en_US">
                <synopsis>
@@ -632,6 +821,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Changes the priority for continuation upon exiting the application.
                        The priority must be a valid priority or label.</para>
                </description>
+               <see-also>
+                       <ref type="agi">set context</ref>
+                       <ref type="agi">set extension</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="set variable" language="en_US">
                <synopsis>
@@ -644,6 +838,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Sets a variable to the current channel.</para>
                </description>
+               <see-also>
+                       <ref type="agi">get variable</ref>
+                       <ref type="agi">get full variable</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="stream file" language="en_US">
                <synopsis>
@@ -681,6 +880,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="agi">control stream file</ref>
+                       <ref type="agi">get option</ref>
+                       <ref type="application">AGI</ref>
                </see-also>
        </agi>
        <agi name="tdd mode" language="en_US">
@@ -699,6 +900,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Enable/Disable TDD transmission/reception on a channel. Returns <literal>1</literal> if
                        successful, or <literal>0</literal> if channel is not TDD-capable.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="verbose" language="en_US">
                <synopsis>
@@ -713,6 +917,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        message system. <replaceable>level</replaceable> is the verbose level (1-4).
                        Always returns <literal>1</literal></para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="wait for digit" language="en_US">
                <synopsis>
@@ -728,6 +935,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        one is received. Use <literal>-1</literal> for the <replaceable>timeout</replaceable> value if
                        you desire the call to block indefinitely.</para>
                </description>
+               <see-also>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech create" language="en_US">
                <synopsis>
@@ -739,6 +949,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Create a speech object to be used by the other Speech AGI commands.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech set" language="en_US">
                <synopsis>
@@ -751,6 +971,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Set an engine-specific setting.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech create</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech destroy" language="en_US">
                <synopsis>
@@ -763,6 +993,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="agi">speech create</ref>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
                </see-also>
        </agi>
        <agi name="speech load grammar" language="en_US">
@@ -776,6 +1013,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Loads the specified grammar as the specified name.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech create</ref>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech unload grammar" language="en_US">
                <synopsis>
@@ -787,6 +1034,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Unloads the specified grammar.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech create</ref>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech activate grammar" language="en_US">
                <synopsis>
@@ -798,6 +1055,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Activates the specified grammar on the speech object.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech create</ref>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech deactivate grammar" language="en_US">
                <synopsis>
@@ -809,6 +1076,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Deactivates the specified grammar on the speech object.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech create</ref>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech recognize</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <agi name="speech recognize" language="en_US">
                <synopsis>
@@ -823,14 +1100,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Plays back given <replaceable>prompt</replaceable> while listening for
                        speech and dtmf.</para>
                </description>
+               <see-also>
+                       <ref type="agi">speech create</ref>
+                       <ref type="agi">speech set</ref>
+                       <ref type="agi">speech destroy</ref>
+                       <ref type="agi">speech load grammar</ref>
+                       <ref type="agi">speech unload grammar</ref>
+                       <ref type="agi">speech activate grammar</ref>
+                       <ref type="agi">speech deactivate grammar</ref>
+                       <ref type="application">AGI</ref>
+               </see-also>
        </agi>
        <application name="AGI" language="en_US">
                <synopsis>
                        Executes an AGI compliant application.
                </synopsis>
                <syntax>
-                       <parameter name="command" required="true" />
+                       <parameter name="command" required="true">
+                               <para>How AGI should be invoked on the channel.</para>
+                       </parameter>
                        <parameter name="args">
+                               <para>Arguments to pass to the AGI script or server.</para>
                                <argument name="arg1" required="true" />
                                <argument name="arg2" multiple="yes" />
                        </parameter>
@@ -839,21 +1129,72 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Executes an Asterisk Gateway Interface compliant
                        program on a channel. AGI allows Asterisk to launch external programs written
                        in any language to control a telephony channel, play audio, read DTMF digits,
-                       etc. by communicating with the AGI protocol on <emphasis>stdin</emphasis> and
-                       <emphasis>stdout</emphasis>. As of <literal>1.6.0</literal>, this channel will
+                       etc. by communicating with the AGI protocol.</para>
+                       <para>The following variants of AGI exist, and are chosen based on the value
+                       passed to <replaceable>command</replaceable>:</para>
+                       <enumlist>
+                               <enum name="AGI">
+                                       <para>The classic variant of AGI, this will launch the script
+                                       specified by <replaceable>command</replaceable> as a new process.
+                                       Communication with the script occurs on <literal>stdin</literal> and
+                                       <literal>stdout</literal>. If the full path to the script is not
+                                       provided, the <directory>astagidir</directory> specified in
+                                       <filename>asterisk.conf</filename> will be used.
+                                       </para>
+                               </enum>
+                               <enum name="FastAGI">
+                                       <para>Connect Asterisk to a FastAGI server using a TCP connection.
+                                       The URI to the FastAGI server should be given in the form
+                                       <literal>[scheme]://host.domain[:port][/script/name]</literal>,
+                                       where <replaceable>scheme</replaceable> is either <literal>agi</literal>
+                                       or <literal>hagi</literal>.</para>
+                                       <para>In the case of <literal>hagi</literal>, an SRV lookup will be
+                                       performed to try to connect to a list of FastAGI servers. The hostname in
+                                       the URI must be prefixed with <literal>_agi._tcp</literal>. prior to the DNS resolution. For
+                                       example, if you specify the URI <literal>hagi://agi.example.com/foo.agi</literal>
+                                       the DNS query would be for <literal>_agi._tcp.agi.example.com</literal>. You
+                                       will need to make sure this resolves correctly.</para>
+                               </enum>
+                               <enum name="AsyncAGI">
+                                       <para>Use AMI to control the channel in AGI. AGI commands can be invoked
+                                       using the <literal>AMI</literal> action, with a variety of AGI specific
+                                       events passed back over the AMI connection. AsyncAGI should be invoked
+                                       by passing <literal>agi:async</literal> to the <replaceable>command</replaceable>
+                                       parameter.</para>
+                               </enum>
+                       </enumlist>
+                       <note>
+                       <para>As of <literal>1.6.0</literal>, this channel will
                        not stop dialplan execution on hangup inside of this application. Dialplan
                        execution will continue normally, even upon hangup until the AGI application
                        signals a desire to stop (either by exiting or, in the case of a net script, by
-                       closing the connection). A locally executed AGI script will receive SIGHUP on
-                       hangup from the channel except when using DeadAGI. A fast AGI server will
-                       correspondingly receive a HANGUP inline with the command dialog. Both of theses
-                       signals may be disabled by setting the <variable>AGISIGHUP</variable> channel
-                       variable to <literal>no</literal> before executing the AGI application.
+                       closing the connection).</para>
+                       <para>A locally executed AGI script will receive <literal>SIGHUP</literal> on
+                       hangup from the channel except when using <literal>DeadAGI</literal>
+                       (or when the channel is already hungup). A fast AGI server will
+                       correspondingly receive a <literal>HANGUP</literal> inline with the command dialog.
+                       Both of these signals may be disabled by setting the <variable>AGISIGHUP</variable>
+                       channel variable to <literal>no</literal> before executing the AGI application.
                        Alternatively, if you would like the AGI application to exit immediately
                        after a channel hangup is detected, set the <variable>AGIEXITONHANGUP</variable>
                        variable to <literal>yes</literal>.</para>
-                       <para>Use the CLI command <literal>agi show commands</literal> to list available agi
-                       commands.</para>
+                       </note>
+                       <example title="AGI invocation examples">
+                               ; Start the AGI script /tmp/my-cool-script.sh, passing it the contents
+                               ; of the channel variable FOO
+                               same => n,AGI(/tmp/my-cool-script.sh,${FOO})
+
+                               ; Start the AGI script my-cool-script.sh located in the astagidir
+                               ; directory, specified in asterisk.conf
+                               same => n,AGI(my-cool-script.sh)
+
+                               ; Connect to the FastAGI server located at 127.0.0.1 and start the script
+                               ; awesome-script
+                               same => n,AGI(agi://127.0.0.1/awesome-script)
+
+                               ; Start AsyncAGI
+                               same => n,AGI(agi:async)
+                       </example>
                        <para>This application sets the following channel variable upon completion:</para>
                        <variablelist>
                                <variable name="AGISTATUS">
@@ -867,8 +1208,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        </variablelist>
                </description>
                <see-also>
+                       <ref type="manager">AGI</ref>
+                       <ref type="managerEvent">AsyncAGIStart</ref>
+                       <ref type="managerEvent">AsyncAGIEnd</ref>
                        <ref type="application">EAGI</ref>
                        <ref type="application">DeadAGI</ref>
+                       <ref type="filename">asterisk.conf</ref>
                </see-also>
        </application>
        <application name="EAGI" language="en_US">
@@ -881,8 +1226,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </syntax>
                <description>
                        <para>Using 'EAGI' provides enhanced AGI, with incoming audio available out of band
-                       on file descriptor 3.</para>
-                       <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/para)" />
+                       on file descriptor 3. In all other respects, it behaves in the same fashion as
+                       AGI. See the documentation for the <literal>AGI</literal> dialplan application for
+                       more information on invoking AGI on a channel.</para>
+                       <para>This application sets the following channel variable upon completion:</para>
                        <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/variablelist)" />
                </description>
                <see-also>
@@ -899,7 +1246,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <xi:include xpointer="xpointer(/docs/application[@name='AGI']/syntax/parameter[@name='args'])" />
                </syntax>
                <description>
-                       <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/para)" />
+                       <warning>
+                               <para>This application is deprecated and may be removed in a future version
+                               of Asterisk. Use the replacement application <literal>AGI</literal> instead
+                               of <literal>DeadAGI</literal>.
+                               </para>
+                       </warning>
+                       <para>Execute AGI on a 'dead' or hungup channel. See the documentation for the
+                       <literal>AGI</literal> dialplan application for more information on invoking
+                       AGI on a channel.</para>
+                       <para>This application sets the following channel variable upon completion:</para>
                        <xi:include xpointer="xpointer(/docs/application[@name='AGI']/description/variablelist)" />
                </description>
                <see-also>
@@ -927,7 +1283,100 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                        <para>Add an AGI command to the execute queue of the channel in Async AGI.</para>
                </description>
+               <see-also>
+                       <ref type="managerEvent">AsyncAGIStart</ref>
+                       <ref type="managerEvent">AsyncAGIExec</ref>
+                       <ref type="managerEvent">AsyncAGIEnd</ref>
+               </see-also>
        </manager>
+       <managerEvent language="en_US" name="AsyncAGIStart">
+               <managerEventInstance class="EVENT_FLAG_AGI">
+                       <synopsis>Raised when a channel starts AsyncAGI command processing.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="Env">
+                                       <para>URL encoded string read from the AsyncAGI server.</para>
+                               </parameter>
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">AsyncAGIEnd</ref>
+                               <ref type="managerEvent">AsyncAGIExec</ref>
+                               <ref type="application">AGI</ref>
+                               <ref type="manager">AGI</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="AsyncAGIEnd">
+               <managerEventInstance class="EVENT_FLAG_AGI">
+                       <synopsis>Raised when a channel stops AsyncAGI command processing.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">AsyncAGIStart</ref>
+                               <ref type="managerEvent">AsyncAGIExec</ref>
+                               <ref type="application">AGI</ref>
+                               <ref type="manager">AGI</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="AsyncAGIExec">
+               <managerEventInstance class="EVENT_FLAG_AGI">
+                       <synopsis>Raised when AsyncAGI completes an AGI command.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="CommandID" required="false">
+                                       <para>Optional command ID sent by the AsyncAGI server to identify the command.</para>
+                               </parameter>
+                               <parameter name="Result">
+                                       <para>URL encoded result string from the executed AGI command.</para>
+                               </parameter>
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">AsyncAGIStart</ref>
+                               <ref type="managerEvent">AsyncAGIEnd</ref>
+                               <ref type="application">AGI</ref>
+                               <ref type="manager">AGI</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="AGIExecStart">
+               <managerEventInstance class="EVENT_FLAG_AGI">
+                       <synopsis>Raised when a received AGI command starts processing.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="Command">
+                                       <para>The AGI command as received from the external source.</para>
+                               </parameter>
+                               <parameter name="CommandId">
+                                       <para>Random identification number assigned to the execution of this command.</para>
+                               </parameter>
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">AGIExecEnd</ref>
+                               <ref type="application">AGI</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="AGIExecEnd">
+               <managerEventInstance class="EVENT_FLAG_AGI">
+                       <synopsis>Raised when a received AGI command completes processing.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='AGIExecStart']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="ResultCode">
+                                       <para>The numeric result code from AGI</para>
+                               </parameter>
+                               <parameter name="Result">
+                                       <para>The text result reason from AGI</para>
+                               </parameter>
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">AGIExecStart</ref>
+                               <ref type="application">AGI</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
  ***/
 
 #define MAX_ARGS 128
@@ -963,6 +1412,66 @@ enum agi_result {
        AGI_RESULT_HANGUP,
 };
 
+static struct ast_manager_event_blob *agi_channel_to_ami(const char *type, struct stasis_message *message)
+{
+       struct ast_channel_blob *obj = stasis_message_data(message);
+       RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
+       RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
+
+       channel_string = ast_manager_build_channel_state_string(obj->snapshot);
+       event_string = ast_manager_str_from_json_object(obj->blob, NULL);
+       if (!channel_string || !event_string) {
+               return NULL;
+       }
+
+       return ast_manager_event_blob_create(EVENT_FLAG_AGI, type,
+               "%s"
+               "%s",
+               ast_str_buffer(channel_string),
+               ast_str_buffer(event_string));
+}
+
+static struct ast_manager_event_blob *agi_exec_start_to_ami(struct stasis_message *message)
+{
+       return agi_channel_to_ami("AGIExecStart", message);
+}
+
+static struct ast_manager_event_blob *agi_exec_end_to_ami(struct stasis_message *message)
+{
+       return agi_channel_to_ami("AGIExecEnd", message);
+}
+
+static struct ast_manager_event_blob *agi_async_start_to_ami(struct stasis_message *message)
+{
+       return agi_channel_to_ami("AsyncAGIStart", message);
+}
+
+static struct ast_manager_event_blob *agi_async_exec_to_ami(struct stasis_message *message)
+{
+       return agi_channel_to_ami("AsyncAGIExec", message);
+}
+
+static struct ast_manager_event_blob *agi_async_end_to_ami(struct stasis_message *message)
+{
+       return agi_channel_to_ami("AsyncAGIEnd", message);
+}
+
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(agi_exec_start_type,
+       .to_ami = agi_exec_start_to_ami,
+       );
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(agi_exec_end_type,
+       .to_ami = agi_exec_end_to_ami,
+       );
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(agi_async_start_type,
+       .to_ami = agi_async_start_to_ami,
+       );
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(agi_async_exec_type,
+       .to_ami = agi_async_exec_to_ami,
+       );
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(agi_async_end_type,
+       .to_ami = agi_async_end_to_ami,
+       );
+
 static agi_command *find_command(const char * const cmds[], int exact);
 
 AST_THREADSTORAGE(agi_buf);
@@ -1269,7 +1778,7 @@ static enum agi_result async_agi_read_frame(struct ast_channel *chan)
        return AGI_RESULT_SUCCESS;
 }
 
-static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], int *efd)
+static enum agi_result launch_asyncagi(struct ast_channel *chan, int argc, char *argv[], int *efd)
 {
 /* This buffer sizes might cause truncation if the AGI command writes more data
    than AGI_BUF_SIZE as result. But let's be serious, is there an AGI command
@@ -1300,6 +1809,7 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
        char ami_buffer[AMI_BUF_SIZE];
        enum agi_result returnstatus = AGI_RESULT_SUCCESS;
        AGI async_agi;
+       RAII_VAR(struct ast_json *, startblob, NULL, ast_json_unref);
 
        if (efd) {
                ast_log(LOG_WARNING, "Async AGI does not support Enhanced AGI yet\n");
@@ -1335,7 +1845,7 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
 
        /* notify possible manager users of a new channel ready to
           receive commands */
-       setup_env(chan, "async", fds[1], 0, 0, NULL);
+       setup_env(chan, "async", fds[1], 0, argc, argv);
        /* read the environment */
        res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
        if (res <= 0) {
@@ -1349,39 +1859,19 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
           care of AGI commands on this channel can decide which AGI commands
           to execute based on the setup info */
        ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, ast_uri_http);
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a channel starts AsyncAGI command processing.</synopsis>
-                       <syntax>
-                               <parameter name="SubEvent">
-                                       <para>A sub event type, specifying the channel AsyncAGI processing status.</para>
-                                       <enumlist>
-                                               <enum name="Start"/>
-                                               <enum name="Exec"/>
-                                               <enum name="End"/>
-                                       </enumlist>
-                               </parameter>
-                               <parameter name="Env">
-                                       <para>URL encoded string read from the AsyncAGI server.</para>
-                               </parameter>
-                       </syntax>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_AGI, "AsyncAGI",
-               "SubEvent: Start\r\n"
-               "Channel: %s\r\n"
-               "Uniqueid: %s\r\n"
-               "Env: %s\r\n",
-               ast_channel_name(chan),
-               ast_channel_uniqueid(chan),
-               ami_buffer);
-       hungup = ast_check_hangup(chan);
+       startblob = ast_json_pack("{s: s}", "Env", ami_buffer);
+
+       ast_channel_publish_cached_blob(chan, agi_async_start_type(), startblob);
+
+       hungup = ast_check_hangup_locked(chan);
+
        for (;;) {
                /*
                 * Process as many commands as we can.  Commands are added via
                 * the manager or the cli threads.
                 */
                while (!hungup) {
+                       RAII_VAR(struct ast_json *, execblob, NULL, ast_json_unref);
                        res = get_agi_cmd(chan, &cmd);
 
                        if (res) {
@@ -1413,40 +1903,13 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
                         */
                        agi_buffer[res] = '\0';
                        ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, ast_uri_http);
-                       if (ast_strlen_zero(cmd->cmd_id)) {
-                               manager_event(EVENT_FLAG_AGI, "AsyncAGI",
-                                       "SubEvent: Exec\r\n"
-                                       "Channel: %s\r\n"
-                                       "Uniqueid: %s\r\n"
-                                       "Result: %s\r\n",
-                                       ast_channel_name(chan),
-                                       ast_channel_uniqueid(chan),
-                                       ami_buffer);
-                       } else {
-                               /*** DOCUMENTATION
-                                       <managerEventInstance>
-                                               <synopsis>Raised when AsyncAGI completes an AGI command.</synopsis>
-                                               <syntax>
-                                                       <parameter name="CommandID" required="false">
-                                                               <para>Optional command ID sent by the AsyncAGI server to identify the command.</para>
-                                                       </parameter>
-                                                       <parameter name="Result">
-                                                               <para>URL encoded result string from the executed AGI command.</para>
-                                                       </parameter>
-                                               </syntax>
-                                       </managerEventInstance>
-                               ***/
-                               manager_event(EVENT_FLAG_AGI, "AsyncAGI",
-                                       "SubEvent: Exec\r\n"
-                                       "Channel: %s\r\n"
-                                       "Uniqueid: %s\r\n"
-                                       "CommandID: %s\r\n"
-                                       "Result: %s\r\n",
-                                       ast_channel_name(chan),
-                                       ast_channel_uniqueid(chan),
-                                       cmd->cmd_id,
-                                       ami_buffer);
+
+                       execblob = ast_json_pack("{s: s}", "Result", ami_buffer);
+                       if (execblob && !ast_strlen_zero(cmd->cmd_id)) {
+                               ast_json_object_set(execblob, "CommandId", ast_json_string_create(cmd->cmd_id));
                        }
+                       ast_channel_publish_cached_blob(chan, agi_async_exec_type(), execblob);
+
                        free_agi_cmd(cmd);
 
                        /*
@@ -1505,17 +1968,7 @@ async_agi_done:
                ast_speech_destroy(async_agi.speech);
        }
        /* notify manager users this channel cannot be controlled anymore by Async AGI */
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a channel stops AsyncAGI command processing.</synopsis>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_AGI, "AsyncAGI",
-               "SubEvent: End\r\n"
-               "Channel: %s\r\n"
-               "Uniqueid: %s\r\n",
-               ast_channel_name(chan),
-               ast_channel_uniqueid(chan));
+       ast_channel_publish_cached_blob(chan, agi_async_end_type(), NULL);
 
 async_agi_abort:
        /* close the pipe */
@@ -1539,12 +1992,61 @@ async_agi_abort:
 #undef AMI_BUF_SIZE
 }
 
+/*!
+ * \internal
+ * \brief Handle the connection that was started by launch_netscript.
+ *
+ * \param agiurl Url that we are trying to connect to.
+ * \param addr Address that host was resolved to.
+ * \param netsockfd File descriptor of socket.
+ *
+ * \retval 0 when connection is succesful.
+ * \retval 1 when there is an error.
+ */
+static int handle_connection(const char *agiurl, const struct ast_sockaddr addr, const int netsockfd)
+{
+       struct pollfd pfds[1];
+       int res, conresult;
+       socklen_t reslen;
+
+       reslen = sizeof(conresult);
+
+       pfds[0].fd = netsockfd;
+       pfds[0].events = POLLOUT;
+
+       while ((res = ast_poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
+               if (errno != EINTR) {
+                       if (!res) {
+                               ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
+                                       agiurl, MAX_AGI_CONNECT);
+                       } else {
+                               ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
+                       }
+
+                       return 1;
+               }
+       }
+
+       if (getsockopt(pfds[0].fd, SOL_SOCKET, SO_ERROR, &conresult, &reslen) < 0) {
+               ast_log(LOG_WARNING, "Connection to %s failed with error: %s\n",
+                       ast_sockaddr_stringify(&addr), strerror(errno));
+               return 1;
+       }
+
+       if (conresult) {
+               ast_log(LOG_WARNING, "Connecting to '%s' failed for url '%s': %s\n",
+                       ast_sockaddr_stringify(&addr), agiurl, strerror(conresult));
+               return 1;
+       }
+
+       return 0;
+}
+
 /* launch_netscript: The fastagi handler.
        FastAGI defaults to port 4573 */
 static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
 {
-       int s = 0, flags, res;
-       struct pollfd pfds[1];
+       int s = 0;
        char *host, *script;
        int num_addrs = 0, i = 0;
        struct ast_sockaddr *addrs;
@@ -1574,24 +2076,21 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
                        continue;
                }
 
-               if ((flags = fcntl(s, F_GETFL)) < 0) {
-                       ast_log(LOG_WARNING, "fcntl(F_GETFL) failed: %s\n", strerror(errno));
+               if (ast_fd_set_flags(s, O_NONBLOCK)) {
                        close(s);
                        continue;
                }
 
-               if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
-                       ast_log(LOG_WARNING, "fnctl(F_SETFL) failed: %s\n", strerror(errno));
-                       close(s);
-                       continue;
-               }
+               if (ast_connect(s, &addrs[i]) && errno == EINPROGRESS) {
+
+                       if (handle_connection(agiurl, addrs[i], s)) {
+                               close(s);
+                               continue;
+                       }
 
-               if (ast_connect(s, &addrs[i]) && (errno != EINPROGRESS)) {
+               } else {
                        ast_log(LOG_WARNING, "Connection to %s failed with unexpected error: %s\n",
-                               ast_sockaddr_stringify(&addrs[i]),
-                               strerror(errno));
-                       close(s);
-                       continue;
+                       ast_sockaddr_stringify(&addrs[i]), strerror(errno));
                }
 
                break;
@@ -1604,20 +2103,6 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
                return AGI_RESULT_FAILURE;
        }
 
-       pfds[0].fd = s;
-       pfds[0].events = POLLOUT;
-       while ((res = ast_poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
-               if (errno != EINTR) {
-                       if (!res) {
-                               ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
-                                       agiurl, MAX_AGI_CONNECT);
-                       } else
-                               ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
-                       close(s);
-                       return AGI_RESULT_FAILURE;
-               }
-       }
-
        if (ast_agi_send(s, NULL, "agi_network: yes\n") < 0) {
                if (errno != EINTR) {
                        ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
@@ -1628,8 +2113,9 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds)
 
        /* If we have a script parameter, relay it to the fastagi server */
        /* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */
-       if (!ast_strlen_zero(script))
+       if (!ast_strlen_zero(script)) {
                ast_agi_send(s, NULL, "agi_network_script: %s\n", script);
+       }
 
        ast_debug(4, "Wow, connected!\n");
        fds[0] = s;
@@ -1710,7 +2196,7 @@ static enum agi_result launch_ha_netscript(char *agiurl, char *argv[], int *fds)
        return AGI_RESULT_FAILURE;
 }
 
-static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
+static enum agi_result launch_script(struct ast_channel *chan, char *script, int argc, char *argv[], int *fds, int *efd, int *opid)
 {
        char tmp[256];
        int pid, toast[2], fromast[2], audio[2], res;
@@ -1723,7 +2209,7 @@ static enum agi_result launch_script(struct ast_channel *chan, char *script, cha
                return (efd == NULL) ? launch_ha_netscript(script, argv, fds) : AGI_RESULT_FAILURE;
        }
        if (!strncasecmp(script, "agi:async", sizeof("agi:async") - 1)) {
-               return launch_asyncagi(chan, argv, efd);
+               return launch_asyncagi(chan, argc, argv, efd);
        }
 
        if (script[0] != '/') {
@@ -1756,9 +2242,8 @@ static enum agi_result launch_script(struct ast_channel *chan, char *script, cha
                        close(toast[1]);
                        return AGI_RESULT_FAILURE;
                }
-               res = fcntl(audio[1], F_GETFL);
-               if (res > -1)
-                       res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
+
+               res = ast_fd_set_flags(audio[1], O_NONBLOCK);
                if (res < 0) {
                        ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
                        close(fromast[0]);
@@ -1901,7 +2386,7 @@ static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, con
                return RESULT_SHOWUSAGE;
        if (sscanf(argv[3], "%30d", &to) != 1)
                return RESULT_SHOWUSAGE;
-       res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
+       res = ast_waitfordigit_full(chan, to, NULL, agi->audio, agi->ctrl);
        ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
        return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
 }
@@ -2014,7 +2499,7 @@ static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc
        long offsetms = 0;
        char offsetbuf[20];
 
-       if (argc < 5 || argc > 9) {
+       if (argc < 5 || argc > 10) {
                return RESULT_SHOWUSAGE;
        }
 
@@ -2038,7 +2523,11 @@ static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc
                suspend = argv[8];
        }
 
-       res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, suspend, NULL, skipms, NULL);
+       if (argc > 9 && (sscanf(argv[9], "%30ld", &offsetms) != 1)) {
+               return RESULT_SHOWUSAGE;
+       }
+
+       res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, suspend, NULL, skipms, &offsetms);
 
        /* If we stopped on one of our stop keys, return 0  */
        if (res > 0 && stop && strchr(stop, res)) {
@@ -2059,7 +2548,7 @@ static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc
        snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
        pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
 
-       ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
+       ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, offsetms);
 
        return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
 }
@@ -2084,15 +2573,16 @@ static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, const
        }
 
        if (!(fs = ast_openstream(chan, argv[2], ast_channel_language(chan)))) {
-               ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
-               return RESULT_SUCCESS;
+               ast_agi_send(agi->fd, chan, "200 result=-1 endpos=%ld\n", sample_offset);
+               return RESULT_FAILURE;
        }
 
        if ((vfs = ast_openvstream(chan, argv[2], ast_channel_language(chan)))) {
                ast_debug(1, "Ooh, found a video stream, too\n");
        }
-
-       ast_verb(3, "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
+       ast_verb(3, "<%s> Playing '%s.%s' (escape_digits=%s) (sample_offset %ld) (language '%s')\n",
+               ast_channel_name(chan), argv[2], ast_format_get_name(ast_channel_writeformat(chan)),
+               edigits, sample_offset, S_OR(ast_channel_language(chan), "default"));
 
        ast_seekstream(fs, 0, SEEK_END);
        max_length = ast_tellstream(fs);
@@ -2144,9 +2634,9 @@ static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, const
        }
 
        if (!(fs = ast_openstream(chan, argv[2], ast_channel_language(chan)))) {
-               ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
+               ast_agi_send(agi->fd, chan, "200 result=-1 endpos=%ld\n", sample_offset);
                ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
-               return RESULT_SUCCESS;
+               return RESULT_FAILURE;
        }
 
        if ((vfs = ast_openvstream(chan, argv[2], ast_channel_language(chan))))
@@ -2176,7 +2666,7 @@ static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, const
 
        /* If the user didnt press a key, wait for digitTimeout*/
        if (res == 0 ) {
-               res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
+               res = ast_waitfordigit_full(chan, timeout, NULL, agi->audio, agi->ctrl);
                /* Make sure the new result is in the escape digits of the GET OPTION */
                if ( !strchr(edigits,res) )
                        res=0;
@@ -2225,11 +2715,37 @@ static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, const
 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, const char * const argv[])
 {
        int res;
+       int sensitivity = AST_SAY_CASE_NONE;
 
-       if (argc != 4)
+       if (argc < 4 || argc > 5) {
                return RESULT_SHOWUSAGE;
+       }
 
-       res = ast_say_character_str_full(chan, argv[2], argv[3], ast_channel_language(chan), agi->audio, agi->ctrl);
+       if (argc > 4) {
+               switch (argv[4][0]) {
+               case 'a':
+               case 'A':
+                       sensitivity = AST_SAY_CASE_ALL;
+                       break;
+               case 'l':
+               case 'L':
+                       sensitivity = AST_SAY_CASE_LOWER;
+                       break;
+               case 'n':
+               case 'N':
+                       sensitivity = AST_SAY_CASE_NONE;
+                       break;
+               case 'u':
+               case 'U':
+                       sensitivity = AST_SAY_CASE_UPPER;
+                       break;
+               case '\0':
+                       break;
+               default:
+                       return RESULT_SHOWUSAGE;
+               }
+       }
+       res = ast_say_character_str_full(chan, argv[2], argv[3], ast_channel_language(chan), sensitivity, agi->audio, agi->ctrl);
        if (res == 1) /* New command */
                return RESULT_SUCCESS;
        ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
@@ -2394,8 +2910,7 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
        int silence = 0;                /* amount of silence to allow */
        int gotsilence = 0;             /* did we timeout for silence? */
        char *silencestr = NULL;
-       struct ast_format rfmt;
-       ast_format_clear(&rfmt);
+       RAII_VAR(struct ast_format *, rfmt, NULL, ao2_cleanup);
 
        /* XXX EAGI FIXME XXX */
 
@@ -2425,8 +2940,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
        }
 
        if (silence > 0) {
-               ast_format_copy(&rfmt, ast_channel_readformat(chan));
-               res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
+               rfmt = ao2_bump(ast_channel_readformat(chan));
+               res = ast_set_read_format(chan, ast_format_slin);
                if (res < 0) {
                        ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
                        ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
@@ -2440,7 +2955,7 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
                }
                ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
        }
-       
+
        /* backward compatibility, if no offset given, arg[6] would have been
         * caught below and taken to be a beep, else if it is a digit then it is a
         * offset */
@@ -2485,8 +3000,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
                        }
                        f = ast_read(chan);
                        if (!f) {
-                               ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
                                ast_closestream(fs);
+                               ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
                                if (sildet)
                                        ast_dsp_free(sildet);
                                return RESULT_FAILURE;
@@ -2500,8 +3015,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
                                        ast_stream_rewind(fs, 200);
                                        ast_truncstream(fs);
                                        sample_offset = ast_tellstream(fs);
-                                       ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset);
                                        ast_closestream(fs);
+                                       ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset);
                                        ast_frfree(f);
                                        if (sildet)
                                                ast_dsp_free(sildet);
@@ -2545,12 +3060,12 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const
                        ast_truncstream(fs);
                        sample_offset = ast_tellstream(fs);
                }
-               ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
                ast_closestream(fs);
+               ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
        }
 
        if (silence > 0) {
-               res = ast_set_read_format(chan, &rfmt);
+               res = ast_set_read_format(chan, rfmt);
                if (res)
                        ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", ast_channel_name(chan));
                ast_dsp_free(sildet);
@@ -2574,7 +3089,9 @@ static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, const
                whentohangup.tv_sec = timeout;
                whentohangup.tv_usec = (timeout - whentohangup.tv_sec) * 1000000.0;
        }
+       ast_channel_lock(chan);
        ast_channel_setwhentohangup_tv(chan, whentohangup);
+       ast_channel_unlock(chan);
        ast_agi_send(agi->fd, chan, "200 result=0\n");
        return RESULT_SUCCESS;
 }
@@ -2618,29 +3135,14 @@ static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, const char
        ast_verb(3, "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argc >= 3 ? argv[2] : "");
 
        if ((app_to_exec = pbx_findapp(argv[1]))) {
+               ast_channel_lock(chan);
                if (!(workaround = ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS))) {
                        ast_set_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS);
                }
-               if (ast_compat_res_agi && argc >= 3 && !ast_strlen_zero(argv[2])) {
-                       char *compat = ast_alloca(strlen(argv[2]) * 2 + 1), *cptr;
-                       const char *vptr;
-                       for (cptr = compat, vptr = argv[2]; *vptr; vptr++) {
-                               if (*vptr == ',') {
-                                       *cptr++ = '\\';
-                                       *cptr++ = ',';
-                               } else if (*vptr == '|') {
-                                       *cptr++ = ',';
-                               } else {
-                                       *cptr++ = *vptr;
-                               }
-                       }
-                       *cptr = '\0';
-                       res = pbx_exec(chan, app_to_exec, compat);
-               } else {
-                       res = pbx_exec(chan, app_to_exec, argc == 2 ? "" : argv[2]);
-               }
+               ast_channel_unlock(chan);
+               res = pbx_exec(chan, app_to_exec, argc == 2 ? "" : argv[2]);
                if (!workaround) {
-                       ast_clear_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS);
+                       ast_channel_clear_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
                }
        } else {
                ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
@@ -2675,16 +3177,18 @@ static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, cons
 
 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, const char * const argv[])
 {
-       struct ast_channel *c;
        if (argc == 2) {
                /* no argument: supply info on the current channel */
-               ast_agi_send(agi->fd, chan, "200 result=%d\n", ast_channel_state(chan));
+               ast_agi_send(agi->fd, chan, "200 result=%u\n", ast_channel_state(chan));
                return RESULT_SUCCESS;
        } else if (argc == 3) {
+               RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+
                /* one argument: look for info on the specified channel */
-               if ((c = ast_channel_get_by_name(argv[2]))) {
-                       ast_agi_send(agi->fd, chan, "200 result=%d\n", ast_channel_state(c));
-                       c = ast_channel_unref(c);
+               if ((msg = stasis_cache_get(ast_channel_cache_by_name(), ast_channel_snapshot_type(), argv[2]))) {
+                       struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
+
+                       ast_agi_send(agi->fd, chan, "200 result=%u\n", snapshot->state);
                        return RESULT_SUCCESS;
                }
                /* if we get this far no channel name matched the argument given */
@@ -2697,6 +3201,10 @@ static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, co
 
 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, const char * const argv[])
 {
+       if (argc != 4) {
+               return RESULT_SHOWUSAGE;
+       }
+
        if (argv[3])
                pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
 
@@ -2801,7 +3309,7 @@ static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, const char
                        break;
                }
        } while (1);
-       
+
        if (res)
                ast_agi_send(agi->fd, chan, "200 result=0\n");
        else
@@ -2901,7 +3409,6 @@ static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, const c
 static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, const char * const argv[])
 {
        struct ast_format_cap *cap;
-       struct ast_format tmpfmt;
 
        /* If a structure already exists, return an error */
        if (agi->speech) {
@@ -2909,16 +3416,16 @@ static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, con
                return RESULT_SUCCESS;
        }
 
-       if (!(cap = ast_format_cap_alloc_nolock())) {
+       if (!(cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
                return RESULT_FAILURE;
        }
-       ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
+       ast_format_cap_append(cap, ast_format_slin, 0);
        if ((agi->speech = ast_speech_new(argv[2], cap))) {
                ast_agi_send(agi->fd, chan, "200 result=1\n");
        } else {
                ast_agi_send(agi->fd, chan, "200 result=0\n");
        }
-       cap = ast_format_cap_destroy(cap);
+       ao2_ref(cap, -1);
 
        return RESULT_SUCCESS;
 }
@@ -3051,7 +3558,6 @@ static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc,
        const char *prompt;
        char dtmf = 0, tmp[4096] = "", *buf = tmp;
        int timeout = 0, offset = 0, res = 0, i = 0;
-       struct ast_format old_read_format;
        long current_offset = 0;
        const char *reason = NULL;
        struct ast_frame *fr = NULL;
@@ -3075,8 +3581,7 @@ static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc,
                offset = atoi(argv[4]);
 
        /* We want frames coming in signed linear */
-       ast_format_copy(&old_read_format, ast_channel_readformat(chan));
-       if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) {
+       if (ast_set_read_format(chan, ast_format_slin)) {
                ast_agi_send(agi->fd, chan, "200 result=0\n");
                return RESULT_SUCCESS;
        }
@@ -3214,15 +3719,15 @@ static struct agi_command commands[] = {
        { { "noop", NULL }, handle_noop, NULL, NULL, 1 },
        { { "receive", "char", NULL }, handle_recvchar, NULL, NULL, 0 },
        { { "receive", "text", NULL }, handle_recvtext, NULL, NULL, 0 },
-       { { "record", "file", NULL }, handle_recordfile, NULL, NULL, 0 }, 
+       { { "record", "file", NULL }, handle_recordfile, NULL, NULL, 0 },
        { { "say", "alpha", NULL }, handle_sayalpha, NULL, NULL, 0},
        { { "say", "digits", NULL }, handle_saydigits, NULL, NULL, 0 },
        { { "say", "number", NULL }, handle_saynumber, NULL, NULL, 0 },
-       { { "say", "phonetic", NULL }, handle_sayphonetic, NULL, NULL, 0}, 
-       { { "say", "date", NULL }, handle_saydate, NULL, NULL, 0}, 
-       { { "say", "time", NULL }, handle_saytime, NULL, NULL, 0}, 
+       { { "say", "phonetic", NULL }, handle_sayphonetic, NULL, NULL, 0},
+       { { "say", "date", NULL }, handle_saydate, NULL, NULL, 0},
+       { { "say", "time", NULL }, handle_saytime, NULL, NULL, 0},
        { { "say", "datetime", NULL }, handle_saydatetime, NULL, NULL, 0},
-       { { "send", "image", NULL }, handle_sendimage, NULL, NULL, 0}, 
+       { { "send", "image", NULL }, handle_sendimage, NULL, NULL, 0},
        { { "send", "text", NULL }, handle_sendtext, NULL, NULL, 0},
        { { "set", "autohangup", NULL }, handle_autohangup, NULL, NULL, 0},
        { { "set", "callerid", NULL }, handle_setcallerid, NULL, NULL, 0},
@@ -3352,10 +3857,9 @@ int AST_OPTIONAL_API_NAME(ast_agi_unregister)(struct ast_module *mod, agi_comman
        }
        AST_RWLIST_TRAVERSE_SAFE_END;
        AST_RWLIST_UNLOCK(&agi_commands);
-       if (unregistered)
+       if (unregistered) {
                ast_verb(2, "AGI Command '%s' unregistered\n",fullcmd);
-       else
-               ast_log(LOG_WARNING, "Unable to unregister command: '%s'!\n",fullcmd);
+       }
        return unregistered;
 }
 
@@ -3506,149 +4010,108 @@ normal:
        return 0;
 }
 
+static void publish_async_exec_end(struct ast_channel *chan, int command_id, const char *command, int result_code, const char *result)
+{
+       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+       blob = ast_json_pack("{s: i, s: s, s: i, s: s}",
+                            "CommandId", command_id,
+                            "Command", command,
+                            "ResultCode", result_code,
+                            "Result", result);
+       ast_channel_publish_cached_blob(chan, agi_exec_end_type(), blob);
+}
+
 static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
 {
-       const char *argv[MAX_ARGS];
+       const char *argv[MAX_ARGS] = {0};
        int argc = MAX_ARGS;
        int res;
        agi_command *c;
-       const char *ami_res;
        char *ami_cmd = ast_strdupa(buf);
+       const char *ami_res;
        int command_id = ast_random();
-       int resultcode;
+       int resultcode = 0;
+       RAII_VAR(struct ast_json *, startblob, NULL, ast_json_unref);
+
+       startblob = ast_json_pack("{s: i, s: s}",
+                            "CommandId", command_id,
+                            "Command", ami_cmd);
+       ast_channel_publish_cached_blob(chan, agi_exec_start_type(), startblob);
 
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a received AGI command starts processing.</synopsis>
-                       <syntax>
-                               <parameter name="SubEvent">
-                                       <para>A sub event type, specifying whether the AGI command has begun or ended.</para>
-                                       <enumlist>
-                                               <enum name="Start"/>
-                                               <enum name="End"/>
-                                       </enumlist>
-                               </parameter>
-                               <parameter name="CommandId">
-                                       <para>Random identification number assigned to the execution of this command.</para>
-                               </parameter>
-                               <parameter name="Command">
-                                       <para>The AGI command as received from the external source.</para>
-                               </parameter>
-                       </syntax>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_AGI, "AGIExec",
-               "SubEvent: Start\r\n"
-               "Channel: %s\r\n"
-               "Uniqueid: %s\r\n"
-               "CommandId: %d\r\n"
-               "Command: %s\r\n",
-               ast_channel_name(chan),
-               ast_channel_uniqueid(chan),
-               command_id,
-               ami_cmd);
        parse_args(buf, &argc, argv);
        c = find_command(argv, 0);
-       if (c && (!dead || (dead && c->dead))) {
-               /* if this command wasn't registered by res_agi, be sure to usecount
-               the module we are using */
-               if (c->mod != ast_module_info->self)
-                       ast_module_ref(c->mod);
-               /* If the AGI command being executed is an actual application (using agi exec)
-               the app field will be updated in pbx_exec via handle_exec */
-               if (ast_channel_cdr(chan) && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
-                       ast_cdr_setapp(ast_channel_cdr(chan), "AGI", buf);
+       if (!c || !ast_module_running_ref(c->mod)) {
+               ami_res = "Invalid or unknown command";
+               resultcode = 510;
 
+               ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
+
+               publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
+               return AGI_RESULT_SUCCESS;
+       }
+
+       if (!dead || (dead && c->dead)) {
                res = c->handler(chan, agi, argc, argv);
-               if (c->mod != ast_module_info->self)
-                       ast_module_unref(c->mod);
                switch (res) {
                case RESULT_SHOWUSAGE:
                        ami_res = "Usage";
                        resultcode = 520;
+
+                       publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
+                       if (ast_strlen_zero(c->usage)) {
+                               ast_agi_send(agi->fd, chan, "520 Invalid command syntax.  Proper usage not available.\n");
+                       } else {
+                               ast_agi_send(agi->fd, chan, "520-Invalid command syntax.  Proper usage follows:\n");
+                               ast_agi_send(agi->fd, chan, "%s\n", c->usage);
+                               ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
+                       }
+
                        break;
                case RESULT_FAILURE:
                        ami_res = "Failure";
                        resultcode = -1;
-                       break;
+
+                       publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
+                       /* The RESULT_FAILURE code is usually because the channel hungup. */
+                       return AGI_RESULT_FAILURE;
                case ASYNC_AGI_BREAK:
+                       ami_res = "Success";
+                       resultcode = 200;
+
+                       publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
+                       return AGI_RESULT_SUCCESS_ASYNC;
                case RESULT_SUCCESS:
                        ami_res = "Success";
                        resultcode = 200;
+
+                       publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
                        break;
                default:
                        ami_res = "Unknown Result";
                        resultcode = 200;
+
+                       publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
                        break;
                }
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when a received AGI command completes processing.</synopsis>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_AGI, "AGIExec",
-                       "SubEvent: End\r\n"
-                       "Channel: %s\r\n"
-                       "Uniqueid: %s\r\n"
-                       "CommandId: %d\r\n"
-                       "Command: %s\r\n"
-                       "ResultCode: %d\r\n"
-                       "Result: %s\r\n",
-                       ast_channel_name(chan),
-                       ast_channel_uniqueid(chan),
-                       command_id,
-                       ami_cmd,
-                       resultcode,
-                       ami_res);
-               switch (res) {
-               case RESULT_SHOWUSAGE:
-                       if (ast_strlen_zero(c->usage)) {
-                               ast_agi_send(agi->fd, chan, "520 Invalid command syntax.  Proper usage not available.\n");
-                       } else {
-                               ast_agi_send(agi->fd, chan, "520-Invalid command syntax.  Proper usage follows:\n");
-                               ast_agi_send(agi->fd, chan, "%s", c->usage);
-                               ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
-                       }
-                       break;
-               case ASYNC_AGI_BREAK:
-                       return AGI_RESULT_SUCCESS_ASYNC;
-               case RESULT_FAILURE:
-                       /* The RESULT_FAILURE code is usually because the channel hungup. */
-                       return AGI_RESULT_FAILURE;
-               default:
-                       break;
-               }
-       } else if (c) {
-               ast_agi_send(agi->fd, chan, "511 Command Not Permitted on a dead channel\n");
-               manager_event(EVENT_FLAG_AGI, "AGIExec",
-                       "SubEvent: End\r\n"
-                       "Channel: %s\r\n"
-                       "Uniqueid: %s\r\n"
-                       "CommandId: %d\r\n"
-                       "Command: %s\r\n"
-                       "ResultCode: 511\r\n"
-                       "Result: Command not permitted on a dead channel\r\n",
-                       ast_channel_name(chan),
-                       ast_channel_uniqueid(chan),
-                       command_id,
-                       ami_cmd);
        } else {
-               ast_agi_send(agi->fd, chan, "510 Invalid or unknown command\n");
-               manager_event(EVENT_FLAG_AGI, "AGIExec",
-                       "SubEvent: End\r\n"
-                       "Channel: %s\r\n"
-                       "Uniqueid: %s\r\n"
-                       "CommandId: %d\r\n"
-                       "Command: %s\r\n"
-                       "ResultCode: 510\r\n"
-                       "Result: Invalid or unknown command\r\n",
-                       ast_channel_name(chan),
-                       ast_channel_uniqueid(chan),
-                       command_id,
-                       ami_cmd);
+               ami_res = "Command Not Permitted on a dead channel or intercept routine";
+               resultcode = 511;
+
+               ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
+
+               publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
        }
+       ast_module_unref(c->mod);
+
        return AGI_RESULT_SUCCESS;
 }
+
 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
 {
        struct ast_channel *c;
@@ -3667,7 +4130,9 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
        const char *sighup_str;
        const char *exit_on_hangup_str;
        int exit_on_hangup;
-       
+       /*! Running in an interception routine is like DeadAGI mode.  No touchy the channel frames. */
+       int in_intercept = ast_channel_get_intercept_mode();
+
        ast_channel_lock(chan);
        sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
        send_sighup = !ast_false(sighup_str);
@@ -3682,7 +4147,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                close(agi->ctrl);
                return AGI_RESULT_FAILURE;
        }
-       
+
        setlinebuf(readf);
        setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
        for (;;) {
@@ -3701,7 +4166,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                        }
                }
                ms = -1;
-               if (dead) {
+               if (dead || in_intercept) {
                        c = ast_waitfor_nandfds(&chan, 0, &agi->ctrl, 1, NULL, &outfd, &ms);
                } else if (!ast_check_hangup(chan)) {
                        c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms);
@@ -3779,10 +4244,10 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
 
                        if (agidebug)
                                ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf);
-                       cmd_status = agi_handle_command(chan, agi, buf, dead);
+                       cmd_status = agi_handle_command(chan, agi, buf, dead || in_intercept);
                        switch (cmd_status) {
                        case AGI_RESULT_FAILURE:
-                               if (dead || !ast_check_hangup(chan)) {
+                               if (dead || in_intercept || !ast_check_hangup(chan)) {
                                        /* The failure was not because of a hangup. */
                                        returnstatus = AGI_RESULT_FAILURE;
                                }
@@ -3798,6 +4263,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                        }
                }
        }
+
        if (agi->speech) {
                ast_speech_destroy(agi->speech);
        }
@@ -3967,9 +4433,6 @@ static int write_htmldump(const char *filename)
 
        AST_RWLIST_RDLOCK(&agi_commands);
        AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
-#ifdef AST_XML_DOCS
-               char *stringptmp;
-#endif
                char *tempstr, *stringp;
 
                if (!command->cmda[0])  /* end ? */
@@ -3982,8 +4445,7 @@ static int write_htmldump(const char *filename)
                fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
                fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
 #ifdef AST_XML_DOCS
-               stringptmp = ast_xmldoc_printable(command->usage, 0);
-               stringp = ast_strdup(stringptmp);
+               stringp = ast_xmldoc_printable(command->usage, 0);
 #else
                stringp = ast_strdup(command->usage);
 #endif
@@ -4001,9 +4463,6 @@ static int write_htmldump(const char *filename)
                fprintf(htmlfile, "</TD></TR>\n");
                fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
                ast_free(stringp);
-#ifdef AST_XML_DOCS
-               ast_free(stringptmp);
-#endif
        }
        AST_RWLIST_UNLOCK(&agi_commands);
        fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
@@ -4062,7 +4521,7 @@ static int agi_exec_full(struct ast_channel *chan, const char *data, int enhance
                        return -1;
        }
 #endif
-       res = launch_script(chan, args.argv[0], args.argv, fds, enhanced ? &efd : NULL, &pid);
+       res = launch_script(chan, args.argv[0], args.argc, args.argv, fds, enhanced ? &efd : NULL, &pid);
        /* Async AGI do not require run_agi(), so just proceed if normal AGI
           or Fast AGI are setup with success. */
        if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
@@ -4113,23 +4572,42 @@ static int agi_exec(struct ast_channel *chan, const char *data)
 static int eagi_exec(struct ast_channel *chan, const char *data)
 {
        int res;
-       struct ast_format readformat;
+       struct ast_format *readformat;
+       struct ast_format *requested_format = NULL;
+       const char *requested_format_name;
 
        if (ast_check_hangup(chan)) {
                ast_log(LOG_ERROR, "EAGI cannot be run on a dead/hungup channel, please use AGI.\n");
                return 0;
        }
-       ast_format_copy(&readformat, ast_channel_readformat(chan));
-       if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) {
+
+       requested_format_name = pbx_builtin_getvar_helper(chan, "EAGI_AUDIO_FORMAT");
+       if (requested_format_name) {
+               requested_format = ast_format_cache_get(requested_format_name);
+               if (requested_format) {
+                       ast_verb(3, "<%s> Setting EAGI audio pipe format to %s\n",
+                                        ast_channel_name(chan), ast_format_get_name(requested_format));
+               } else {
+                       ast_log(LOG_ERROR, "Could not find requested format: %s\n", requested_format_name);
+               }
+       }
+
+       readformat = ao2_bump(ast_channel_readformat(chan));
+       if (ast_set_read_format(chan, requested_format ?: ast_format_slin)) {
                ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", ast_channel_name(chan));
+               ao2_cleanup(requested_format);
+               ao2_cleanup(readformat);
                return -1;
        }
        res = agi_exec_full(chan, data, 1, 0);
        if (!res) {
-               if (ast_set_read_format(chan, &readformat)) {
-                       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", ast_channel_name(chan), ast_getformatname(&readformat));
+               if (ast_set_read_format(chan, readformat)) {
+                       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", ast_channel_name(chan),
+                               ast_format_get_name(readformat));
                }
        }
+       ao2_cleanup(requested_format);
+       ao2_cleanup(readformat);
        return res;
 }
 
@@ -4188,34 +4666,56 @@ AST_TEST_DEFINE(test_agi_null_docs)
 
 static int unload_module(void)
 {
+       STASIS_MESSAGE_TYPE_CLEANUP(agi_exec_start_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(agi_exec_end_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(agi_async_start_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(agi_async_exec_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(agi_async_end_type);
+
        ast_cli_unregister_multiple(cli_agi, ARRAY_LEN(cli_agi));
-       /* we can safely ignore the result of ast_agi_unregister_multiple() here, since it cannot fail, as
-          we know that these commands were registered by this module and are still registered
-       */
-       (void) ast_agi_unregister_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
+       ast_agi_unregister_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
        ast_unregister_application(eapp);
        ast_unregister_application(deadapp);
        ast_manager_unregister("AGI");
+       ast_unregister_application(app);
        AST_TEST_UNREGISTER(test_agi_null_docs);
-       return ast_unregister_application(app);
+       return 0;
 }
 
 static int load_module(void)
 {
-       ast_cli_register_multiple(cli_agi, ARRAY_LEN(cli_agi));
-       /* we can safely ignore the result of ast_agi_register_multiple() here, since it cannot fail, as
-          no other commands have been registered yet
-       */
-       (void) ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
-       ast_register_application_xml(deadapp, deadagi_exec);
-       ast_register_application_xml(eapp, eagi_exec);
-       ast_manager_register_xml("AGI", EVENT_FLAG_AGI, action_add_agi_cmd);
+       int err = 0;
+
+       err |= STASIS_MESSAGE_TYPE_INIT(agi_exec_start_type);
+       err |= STASIS_MESSAGE_TYPE_INIT(agi_exec_end_type);
+       err |= STASIS_MESSAGE_TYPE_INIT(agi_async_start_type);
+       err |= STASIS_MESSAGE_TYPE_INIT(agi_async_exec_type);
+       err |= STASIS_MESSAGE_TYPE_INIT(agi_async_end_type);
+
+       err |= ast_cli_register_multiple(cli_agi, ARRAY_LEN(cli_agi));
+       err |= ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
+       err |= ast_register_application_xml(deadapp, deadagi_exec);
+       err |= ast_register_application_xml(eapp, eagi_exec);
+       err |= ast_manager_register_xml("AGI", EVENT_FLAG_AGI, action_add_agi_cmd);
+       err |= ast_register_application_xml(app, agi_exec);
+
        AST_TEST_REGISTER(test_agi_null_docs);
-       return ast_register_application_xml(app, agi_exec);
+
+       if (err) {
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       /* For Optional API. */
+       ast_module_shutdown_ref(AST_MODULE_SELF);
+
+       return AST_MODULE_LOAD_SUCCESS;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk Gateway Interface (AGI)",
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_APP_DEPEND,
-               );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_APP_DEPEND,
+       .requires = "res_speech",
+);