Mon Sep 20 2010 00:23:32

Asterisk developer's documentation


res_agi.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief AGI - the Asterisk Gateway Interface
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \todo Convert the rest of the AGI commands over to XML documentation
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 254447 $")
00031 
00032 #include <math.h>
00033 #include <signal.h>
00034 #include <sys/time.h>
00035 #include <sys/wait.h>
00036 #include <sys/stat.h>
00037 #include <pthread.h>
00038 
00039 #include "asterisk/paths.h"   /* use many ast_config_AST_*_DIR */
00040 #include "asterisk/network.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/astdb.h"
00046 #include "asterisk/callerid.h"
00047 #include "asterisk/cli.h"
00048 #include "asterisk/image.h"
00049 #include "asterisk/say.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/dsp.h"
00052 #include "asterisk/musiconhold.h"
00053 #include "asterisk/utils.h"
00054 #include "asterisk/lock.h"
00055 #include "asterisk/strings.h"
00056 #include "asterisk/manager.h"
00057 #include "asterisk/ast_version.h"
00058 #include "asterisk/speech.h"
00059 #include "asterisk/manager.h"
00060 #include "asterisk/features.h"
00061 #include "asterisk/term.h"
00062 #include "asterisk/xmldoc.h"
00063 
00064 #define AST_API_MODULE
00065 #include "asterisk/agi.h"
00066 
00067 /*** DOCUMENTATION
00068    <agi name="answer" language="en_US">
00069       <synopsis>
00070          Answer channel
00071       </synopsis>
00072       <syntax />
00073       <description>
00074          <para>Answers channel if not already in answer state. Returns <literal>-1</literal> on
00075          channel failure, or <literal>0</literal> if successful.</para>
00076       </description>
00077       <see-also>
00078          <ref type="agi">hangup</ref>
00079       </see-also>
00080    </agi>
00081    <agi name="asyncagi break" language="en_US">
00082       <synopsis>
00083          Interrupts Async AGI
00084       </synopsis>
00085       <syntax />
00086       <description>
00087          <para>Interrupts expected flow of Async AGI commands and returns control to previous source
00088          (typically, the PBX dialplan).</para>
00089       </description>
00090       <see-also>
00091          <ref type="agi">hangup</ref>
00092       </see-also>
00093    </agi>
00094    <agi name="channel status" language="en_US">
00095       <synopsis>
00096          Returns status of the connected channel.
00097       </synopsis>
00098       <syntax>
00099          <parameter name="channelname" />
00100       </syntax>
00101       <description>
00102          <para>Returns the status of the specified <replaceable>channelname</replaceable>.
00103          If no channel name is given then returns the status of the current channel.</para>
00104          <para>Return values:</para>
00105          <enumlist>
00106             <enum name="0">
00107                <para>Channel is down and available.</para>
00108             </enum>
00109             <enum name="1">
00110                <para>Channel is down, but reserved.</para>
00111             </enum>
00112             <enum name="2">
00113                <para>Channel is off hook.</para>
00114             </enum>
00115             <enum name="3">
00116                <para>Digits (or equivalent) have been dialed.</para>
00117             </enum>
00118             <enum name="4">
00119                <para>Line is ringing.</para>
00120             </enum>
00121             <enum name="5">
00122                <para>Remote end is ringing.</para>
00123             </enum>
00124             <enum name="6">
00125                <para>Line is up.</para>
00126             </enum>
00127             <enum name="7">
00128                <para>Line is busy.</para>
00129             </enum>
00130          </enumlist>
00131       </description>
00132    </agi>
00133    <agi name="database del" language="en_US">
00134       <synopsis>
00135          Removes database key/value
00136       </synopsis>
00137       <syntax>
00138          <parameter name="family" required="true" />
00139          <parameter name="key" required="true" />
00140       </syntax>
00141       <description>
00142          <para>Deletes an entry in the Asterisk database for a given
00143          <replaceable>family</replaceable> and <replaceable>key</replaceable>.</para>
00144          <para>Returns <literal>1</literal> if successful, <literal>0</literal>
00145          otherwise.</para>
00146       </description>
00147    </agi>
00148    <agi name="database deltree" language="en_US">
00149       <synopsis>
00150          Removes database keytree/value
00151       </synopsis>
00152       <syntax>
00153          <parameter name="family" required="true" />
00154          <parameter name="keytree" />
00155       </syntax>
00156       <description>
00157          <para>Deletes a <replaceable>family</replaceable> or specific <replaceable>keytree</replaceable>
00158          within a <replaceable>family</replaceable> in the Asterisk database.</para>
00159          <para>Returns <literal>1</literal> if successful, <literal>0</literal> otherwise.</para>
00160       </description>
00161    </agi>
00162    <agi name="database get" language="en_US">
00163       <synopsis>
00164          Gets database value
00165       </synopsis>
00166       <syntax>
00167          <parameter name="family" required="true" />
00168          <parameter name="key" required="true" />
00169       </syntax>
00170       <description>
00171          <para>Retrieves an entry in the Asterisk database for a given <replaceable>family</replaceable>
00172          and <replaceable>key</replaceable>.</para>
00173          <para>Returns <literal>0</literal> if <replaceable>key</replaceable> is not set.
00174          Returns <literal>1</literal> if <replaceable>key</replaceable> is set and returns the variable
00175          in parenthesis.</para>
00176          <para>Example return code: 200 result=1 (testvariable)</para>
00177       </description>
00178    </agi>
00179    <agi name="database put" language="en_US">
00180       <synopsis>
00181          Adds/updates database value
00182       </synopsis>
00183       <syntax>
00184          <parameter name="family" required="true" />
00185          <parameter name="key" required="true" />
00186          <parameter name="value" required="true" />
00187       </syntax>
00188       <description>
00189          <para>Adds or updates an entry in the Asterisk database for a given
00190          <replaceable>family</replaceable>, <replaceable>key</replaceable>, and
00191          <replaceable>value</replaceable>.</para>
00192          <para>Returns <literal>1</literal> if successful, <literal>0</literal> otherwise.</para>
00193       </description>
00194    </agi>
00195    <agi name="exec" language="en_US">
00196       <synopsis>
00197          Executes a given Application
00198       </synopsis>
00199       <syntax>
00200          <parameter name="application" required="true" />
00201          <parameter name="options" required="true" />
00202       </syntax>
00203       <description>
00204          <para>Executes <replaceable>application</replaceable> with given
00205          <replaceable>options</replaceable>.</para>
00206          <para>Returns whatever the <replaceable>application</replaceable> returns, or
00207          <literal>-2</literal> on failure to find <replaceable>application</replaceable>.</para>
00208       </description>
00209    </agi>
00210    <agi name="get data" language="en_US">
00211       <synopsis>
00212          Prompts for DTMF on a channel
00213       </synopsis>
00214       <syntax>
00215          <parameter name="file" required="true" />
00216          <parameter name="timeout" />
00217          <parameter name="maxdigits" />
00218       </syntax>
00219       <description>
00220          <para>Stream the given <replaceable>file</replaceable>, and receive DTMF data.</para>
00221          <para>Returns the digits received from the channel at the other end.</para>
00222       </description>
00223    </agi>
00224    <agi name="get full variable" language="en_US">
00225       <synopsis>
00226          Evaluates a channel expression
00227       </synopsis>
00228       <syntax>
00229          <parameter name="variablename" required="true" />
00230          <parameter name="channel name" />
00231       </syntax>
00232       <description>
00233          <para>Returns <literal>0</literal> if <replaceable>variablename</replaceable> is not set
00234          or channel does not exist. Returns <literal>1</literal> if <replaceable>variablename</replaceable>
00235          is set and returns the variable in parenthesis. Understands complex variable names and builtin
00236          variables, unlike GET VARIABLE.</para>
00237          <para>Example return code: 200 result=1 (testvariable)</para>
00238       </description>
00239    </agi>
00240    <agi name="get option" language="en_US">
00241       <synopsis>
00242          Stream file, prompt for DTMF, with timeout.
00243       </synopsis>
00244       <syntax>
00245          <parameter name="filename" required="true" />
00246          <parameter name="escape_digits" required="true" />
00247          <parameter name="timeout" />
00248       </syntax>
00249       <description>
00250          <para>Behaves similar to STREAM FILE but used with a timeout option.</para>
00251       </description>
00252       <see-also>
00253          <ref type="agi">stream file</ref>
00254       </see-also>
00255    </agi>
00256    <agi name="get variable" language="en_US">
00257       <synopsis>
00258          Gets a channel variable.
00259       </synopsis>
00260       <syntax>
00261          <parameter name="variablename" required="true" />
00262       </syntax>
00263       <description>
00264          <para>Returns <literal>0</literal> if <replaceable>variablename</replaceable> is not set.
00265          Returns <literal>1</literal> if <replaceable>variablename</replaceable> is set and returns
00266          the variable in parentheses.</para>
00267          <para>Example return code: 200 result=1 (testvariable)</para>
00268       </description>
00269    </agi>
00270    <agi name="hangup" language="en_US">
00271       <synopsis>
00272          Hangup the current channel.
00273       </synopsis>
00274       <syntax>
00275          <parameter name="channelname" />
00276       </syntax>
00277       <description>
00278          <para>Hangs up the specified channel. If no channel name is given, hangs
00279          up the current channel</para>
00280       </description>
00281    </agi>
00282    <agi name="noop" language="en_US">
00283       <synopsis>
00284          Does nothing.
00285       </synopsis>
00286       <syntax />
00287       <description>
00288          <para>Does nothing.</para>
00289       </description>
00290    </agi>
00291    <agi name="set music" language="en_US">
00292       <synopsis>
00293          Enable/Disable Music on hold generator
00294       </synopsis>
00295       <syntax>
00296          <parameter required="true">
00297             <enumlist>
00298                <enum>
00299                   <parameter name="on" literal="true" required="true" />
00300                </enum>
00301                <enum>
00302                   <parameter name="off" literal="true" required="true" />
00303                </enum>
00304             </enumlist>
00305          </parameter>
00306          <parameter name="class" required="true" />
00307       </syntax>
00308       <description>
00309          <para>Enables/Disables the music on hold generator. If <replaceable>class</replaceable>
00310          is not specified, then the <literal>default</literal> music on hold class will be
00311          used.</para>
00312          <para>Always returns <literal>0</literal>.</para>
00313       </description>
00314    </agi>
00315  ***/
00316 
00317 #define MAX_ARGS 128
00318 #define MAX_CMD_LEN 80
00319 #define AGI_NANDFS_RETRY 3
00320 #define AGI_BUF_LEN 2048
00321 
00322 static char *app = "AGI";
00323 
00324 static char *eapp = "EAGI";
00325 
00326 static char *deadapp = "DeadAGI";
00327 
00328 static char *synopsis = "Executes an AGI compliant application";
00329 static char *esynopsis = "Executes an EAGI compliant application";
00330 static char *deadsynopsis = "Executes AGI on a hungup channel";
00331 
00332 static char *descrip =
00333 "  [E|Dead]AGI(command,args): Executes an Asterisk Gateway Interface compliant\n"
00334 "program on a channel. AGI allows Asterisk to launch external programs written\n"
00335 "in any language to control a telephony channel, play audio, read DTMF digits,\n"
00336 "etc. by communicating with the AGI protocol on stdin and stdout.\n"
00337 "  As of 1.6.0, this channel will not stop dialplan execution on hangup inside\n"
00338 "of this application. Dialplan execution will continue normally, even upon\n"
00339 "hangup until the AGI application signals a desire to stop (either by exiting\n"
00340 "or, in the case of a net script, by closing the connection).\n"
00341 "  A locally executed AGI script will receive SIGHUP on hangup from the channel\n"
00342 "except when using DeadAGI. A fast AGI server will correspondingly receive a\n"
00343 "HANGUP in OOB data. Both of these signals may be disabled by setting the\n"
00344 "AGISIGHUP channel variable to \"no\" before executing the AGI application.\n"
00345 "  Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
00346 "on file descriptor 3.\n\n"
00347 "  Use the CLI command 'agi show commnands' to list available agi commands.\n"
00348 "  This application sets the following channel variable upon completion:\n"
00349 "     AGISTATUS      The status of the attempt to the run the AGI script\n"
00350 "                    text string, one of SUCCESS | FAILURE | NOTFOUND | HANGUP\n";
00351 
00352 static int agidebug = 0;
00353 
00354 #define TONE_BLOCK_SIZE 200
00355 
00356 /* Max time to connect to an AGI remote host */
00357 #define MAX_AGI_CONNECT 2000
00358 
00359 #define AGI_PORT 4573
00360 
00361 enum agi_result {
00362    AGI_RESULT_FAILURE = -1,
00363    AGI_RESULT_SUCCESS,
00364    AGI_RESULT_SUCCESS_FAST,
00365    AGI_RESULT_SUCCESS_ASYNC,
00366    AGI_RESULT_NOTFOUND,
00367    AGI_RESULT_HANGUP,
00368 };
00369 
00370 static agi_command *find_command(char *cmds[], int exact);
00371 
00372 AST_THREADSTORAGE(agi_buf);
00373 #define AGI_BUF_INITSIZE 256
00374 
00375 int ast_agi_send(int fd, struct ast_channel *chan, char *fmt, ...)
00376 {
00377    int res = 0;
00378    va_list ap;
00379    struct ast_str *buf;
00380 
00381    if (!(buf = ast_str_thread_get(&agi_buf, AGI_BUF_INITSIZE)))
00382       return -1;
00383 
00384    va_start(ap, fmt);
00385    res = ast_str_set_va(&buf, 0, fmt, ap);
00386    va_end(ap);
00387 
00388    if (res == -1) {
00389       ast_log(LOG_ERROR, "Out of memory\n");
00390       return -1;
00391    }
00392 
00393    if (agidebug) {
00394       if (chan) {
00395          ast_verbose("<%s>AGI Tx >> %s", chan->name, ast_str_buffer(buf));
00396       } else {
00397          ast_verbose("AGI Tx >> %s", ast_str_buffer(buf));
00398       }
00399    }
00400 
00401    return ast_carefulwrite(fd, ast_str_buffer(buf), ast_str_strlen(buf), 100);
00402 }
00403 
00404 /* linked list of AGI commands ready to be executed by Async AGI */
00405 struct agi_cmd {
00406    char *cmd_buffer;
00407    char *cmd_id;
00408    AST_LIST_ENTRY(agi_cmd) entry;
00409 };
00410 
00411 static void free_agi_cmd(struct agi_cmd *cmd)
00412 {
00413    ast_free(cmd->cmd_buffer);
00414    ast_free(cmd->cmd_id);
00415    ast_free(cmd);
00416 }
00417 
00418 /* AGI datastore destructor */
00419 static void agi_destroy_commands_cb(void *data)
00420 {
00421    struct agi_cmd *cmd;
00422    AST_LIST_HEAD(, agi_cmd) *chan_cmds = data;
00423    AST_LIST_LOCK(chan_cmds);
00424    while ( (cmd = AST_LIST_REMOVE_HEAD(chan_cmds, entry)) ) {
00425       free_agi_cmd(cmd);
00426    }
00427    AST_LIST_UNLOCK(chan_cmds);
00428    AST_LIST_HEAD_DESTROY(chan_cmds);
00429    ast_free(chan_cmds);
00430 }
00431 
00432 /* channel datastore to keep the queue of AGI commands in the channel */
00433 static const struct ast_datastore_info agi_commands_datastore_info = {
00434    .type = "AsyncAGI",
00435    .destroy = agi_destroy_commands_cb
00436 };
00437 
00438 static const char mandescr_asyncagi[] =
00439 "Description: Add an AGI command to the execute queue of the channel in Async AGI\n"
00440 "Variables:\n"
00441 "  *Channel: Channel that is currently in Async AGI\n"
00442 "  *Command: Application to execute\n"
00443 "   CommandID: comand id. This will be sent back in CommandID header of AsyncAGI exec event notification\n"
00444 "\n";
00445 
00446 static struct agi_cmd *get_agi_cmd(struct ast_channel *chan)
00447 {
00448    struct ast_datastore *store;
00449    struct agi_cmd *cmd;
00450    AST_LIST_HEAD(, agi_cmd) *agi_commands;
00451 
00452    ast_channel_lock(chan);
00453    store = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
00454    ast_channel_unlock(chan);
00455    if (!store) {
00456       ast_log(LOG_ERROR, "Hu? datastore disappeared at Async AGI on Channel %s!\n", chan->name);
00457       return NULL;
00458    }
00459    agi_commands = store->data;
00460    AST_LIST_LOCK(agi_commands);
00461    cmd = AST_LIST_REMOVE_HEAD(agi_commands, entry);
00462    AST_LIST_UNLOCK(agi_commands);
00463    return cmd;
00464 }
00465 
00466 /* channel is locked when calling this one either from the CLI or manager thread */
00467 static int add_agi_cmd(struct ast_channel *chan, const char *cmd_buff, const char *cmd_id)
00468 {
00469    struct ast_datastore *store;
00470    struct agi_cmd *cmd;
00471    AST_LIST_HEAD(, agi_cmd) *agi_commands;
00472 
00473    store = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
00474    if (!store) {
00475       ast_log(LOG_WARNING, "Channel %s is not at Async AGI.\n", chan->name);
00476       return -1;
00477    }
00478    agi_commands = store->data;
00479    cmd = ast_calloc(1, sizeof(*cmd));
00480    if (!cmd) {
00481       return -1;
00482    }
00483    cmd->cmd_buffer = ast_strdup(cmd_buff);
00484    if (!cmd->cmd_buffer) {
00485       ast_free(cmd);
00486       return -1;
00487    }
00488    cmd->cmd_id = ast_strdup(cmd_id);
00489    if (!cmd->cmd_id) {
00490       ast_free(cmd->cmd_buffer);
00491       ast_free(cmd);
00492       return -1;
00493    }
00494    AST_LIST_LOCK(agi_commands);
00495    AST_LIST_INSERT_TAIL(agi_commands, cmd, entry);
00496    AST_LIST_UNLOCK(agi_commands);
00497    return 0;
00498 }
00499 
00500 static int add_to_agi(struct ast_channel *chan)
00501 {
00502    struct ast_datastore *datastore;
00503    AST_LIST_HEAD(, agi_cmd) *agi_cmds_list;
00504 
00505    /* check if already on AGI */
00506    ast_channel_lock(chan);
00507    datastore = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
00508    ast_channel_unlock(chan);
00509    if (datastore) {
00510       /* we already have an AGI datastore, let's just
00511          return success */
00512       return 0;
00513    }
00514 
00515    /* the channel has never been on Async AGI,
00516       let's allocate it's datastore */
00517    datastore = ast_datastore_alloc(&agi_commands_datastore_info, "AGI");
00518    if (!datastore) {
00519       return -1;
00520    }
00521    agi_cmds_list = ast_calloc(1, sizeof(*agi_cmds_list));
00522    if (!agi_cmds_list) {
00523       ast_log(LOG_ERROR, "Unable to allocate Async AGI commands list.\n");
00524       ast_datastore_free(datastore);
00525       return -1;
00526    }
00527    datastore->data = agi_cmds_list;
00528    AST_LIST_HEAD_INIT(agi_cmds_list);
00529    ast_channel_lock(chan);
00530    ast_channel_datastore_add(chan, datastore);
00531    ast_channel_unlock(chan);
00532    return 0;
00533 }
00534 
00535 /*!
00536  * \brief CLI command to add applications to execute in Async AGI
00537  * \param e
00538  * \param cmd
00539  * \param a
00540  *
00541  * \retval CLI_SUCCESS on success
00542  * \retval NULL when init or tab completion is used
00543 */
00544 static char *handle_cli_agi_add_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00545 {
00546    struct ast_channel *chan;
00547    switch (cmd) {
00548    case CLI_INIT:
00549       e->command = "agi exec";
00550       e->usage = "Usage: agi exec <channel name> <app and arguments> [id]\n"
00551             "       Add AGI command to the execute queue of the specified channel in Async AGI\n";
00552       return NULL;
00553    case CLI_GENERATE:
00554       if (a->pos == 2)
00555          return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00556       return NULL;
00557    }
00558 
00559    if (a->argc < 4)
00560       return CLI_SHOWUSAGE;
00561    chan = ast_get_channel_by_name_locked(a->argv[2]);
00562    if (!chan) {
00563       ast_log(LOG_WARNING, "Channel %s does not exists or cannot lock it\n", a->argv[2]);
00564       return CLI_FAILURE;
00565    }
00566    if (add_agi_cmd(chan, a->argv[3], (a->argc > 4 ? a->argv[4] : ""))) {
00567       ast_log(LOG_WARNING, "failed to add AGI command to queue of channel %s\n", chan->name);
00568       ast_channel_unlock(chan);
00569       return CLI_FAILURE;
00570    }
00571    ast_log(LOG_DEBUG, "Added AGI command to channel %s queue\n", chan->name);
00572    ast_channel_unlock(chan);
00573    return CLI_SUCCESS;
00574 }
00575 
00576 /*!
00577  * \brief Add a new command to execute by the Async AGI application
00578  * \param s
00579  * \param m
00580  *
00581  * It will append the application to the specified channel's queue
00582  * if the channel is not inside Async AGI application it will return an error
00583  * \retval 0 on success or incorrect use
00584  * \retval 1 on failure to add the command ( most likely because the channel
00585  * is not in Async AGI loop )
00586 */
00587 static int action_add_agi_cmd(struct mansession *s, const struct message *m)
00588 {
00589    const char *channel = astman_get_header(m, "Channel");
00590    const char *cmdbuff = astman_get_header(m, "Command");
00591    const char *cmdid   = astman_get_header(m, "CommandID");
00592    struct ast_channel *chan;
00593    char buf[256];
00594    if (ast_strlen_zero(channel) || ast_strlen_zero(cmdbuff)) {
00595       astman_send_error(s, m, "Both, Channel and Command are *required*");
00596       return 0;
00597    }
00598    chan = ast_get_channel_by_name_locked(channel);
00599    if (!chan) {
00600       snprintf(buf, sizeof(buf), "Channel %s does not exists or cannot get its lock", channel);
00601       astman_send_error(s, m, buf);
00602       return 0;
00603    }
00604    if (add_agi_cmd(chan, cmdbuff, cmdid)) {
00605       snprintf(buf, sizeof(buf), "Failed to add AGI command to channel %s queue", chan->name);
00606       astman_send_error(s, m, buf);
00607       ast_channel_unlock(chan);
00608       return 0;
00609    }
00610    astman_send_ack(s, m, "Added AGI command to queue");
00611    ast_channel_unlock(chan);
00612    return 0;
00613 }
00614 
00615 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead);
00616 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[]);
00617 static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], int *efd)
00618 {
00619 /* This buffer sizes might cause truncation if the AGI command writes more data
00620    than AGI_BUF_SIZE as result. But let's be serious, is there an AGI command
00621    that writes a response larger than 1024 bytes?, I don't think so, most of
00622    them are just result=blah stuff. However probably if GET VARIABLE is called
00623    and the variable has large amount of data, that could be a problem. We could
00624    make this buffers dynamic, but let's leave that as a second step.
00625 
00626    AMI_BUF_SIZE is twice AGI_BUF_SIZE just for the sake of choosing a safe
00627    number. Some characters of AGI buf will be url encoded to be sent to manager
00628    clients.  An URL encoded character will take 3 bytes, but again, to cause
00629    truncation more than about 70% of the AGI buffer should be URL encoded for
00630    that to happen.  Not likely at all.
00631 
00632    On the other hand. I wonder if read() could eventually return less data than
00633    the amount already available in the pipe? If so, how to deal with that?
00634    So far, my tests on Linux have not had any problems.
00635  */
00636 #define AGI_BUF_SIZE 1024
00637 #define AMI_BUF_SIZE 2048
00638    struct ast_frame *f;
00639    struct agi_cmd *cmd;
00640    int res, fds[2];
00641    int timeout = 100;
00642    char agi_buffer[AGI_BUF_SIZE + 1];
00643    char ami_buffer[AMI_BUF_SIZE];
00644    enum agi_result returnstatus = AGI_RESULT_SUCCESS_ASYNC;
00645    AGI async_agi;
00646 
00647    if (efd) {
00648       ast_log(LOG_WARNING, "Async AGI does not support Enhanced AGI yet\n");
00649       return AGI_RESULT_FAILURE;
00650    }
00651 
00652    /* add AsyncAGI datastore to the channel */
00653    if (add_to_agi(chan)) {
00654       ast_log(LOG_ERROR, "failed to start Async AGI on channel %s\n", chan->name);
00655       return AGI_RESULT_FAILURE;
00656    }
00657 
00658    /* this pipe allows us to create a "fake" AGI struct to use
00659       the AGI commands */
00660    res = pipe(fds);
00661    if (res) {
00662       ast_log(LOG_ERROR, "failed to create Async AGI pipe\n");
00663       /* intentionally do not remove datastore, added with
00664          add_to_agi(), from channel. It will be removed when
00665          the channel is hung up anyways */
00666       return AGI_RESULT_FAILURE;
00667    }
00668 
00669    /* handlers will get the pipe write fd and we read the AGI responses
00670       from the pipe read fd */
00671    async_agi.fd = fds[1];
00672    async_agi.ctrl = fds[1];
00673    async_agi.audio = -1; /* no audio support */
00674    async_agi.fast = 0;
00675    async_agi.speech = NULL;
00676 
00677    /* notify possible manager users of a new channel ready to
00678       receive commands */
00679    setup_env(chan, "async", fds[1], 0, 0, NULL);
00680    /* read the environment */
00681    res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
00682    if (!res) {
00683       ast_log(LOG_ERROR, "failed to read from Async AGI pipe on channel %s\n", chan->name);
00684       returnstatus = AGI_RESULT_FAILURE;
00685       goto quit;
00686    }
00687    agi_buffer[res] = '\0';
00688    /* encode it and send it thru the manager so whoever is going to take
00689       care of AGI commands on this channel can decide which AGI commands
00690       to execute based on the setup info */
00691    ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, 1);
00692    manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: Start\r\nChannel: %s\r\nEnv: %s\r\n", chan->name, ami_buffer);
00693    while (1) {
00694       /* bail out if we need to hangup */
00695       if (ast_check_hangup(chan)) {
00696          ast_log(LOG_DEBUG, "ast_check_hangup returned true on chan %s\n", chan->name);
00697          break;
00698       }
00699       /* retrieve a command
00700          (commands are added via the manager or the cli threads) */
00701       cmd = get_agi_cmd(chan);
00702       if (cmd) {
00703          /* OK, we have a command, let's call the
00704             command handler. */
00705          res = agi_handle_command(chan, &async_agi, cmd->cmd_buffer, 0);
00706          if (res < 0) {
00707             free_agi_cmd(cmd);
00708             break;
00709          }
00710          /* the command handler must have written to our fake
00711             AGI struct fd (the pipe), let's read the response */
00712          res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
00713          if (!res) {
00714             returnstatus = AGI_RESULT_FAILURE;
00715             ast_log(LOG_ERROR, "failed to read from AsyncAGI pipe on channel %s\n", chan->name);
00716             free_agi_cmd(cmd);
00717             break;
00718          }
00719          /* we have a response, let's send the response thru the
00720             manager. Include the CommandID if it was specified
00721             when the command was added */
00722          agi_buffer[res] = '\0';
00723          ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, 1);
00724          if (ast_strlen_zero(cmd->cmd_id))
00725             manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: Exec\r\nChannel: %s\r\nResult: %s\r\n", chan->name, ami_buffer);
00726          else
00727             manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: Exec\r\nChannel: %s\r\nCommandID: %s\r\nResult: %s\r\n", chan->name, cmd->cmd_id, ami_buffer);
00728          free_agi_cmd(cmd);
00729       } else {
00730          /* no command so far, wait a bit for a frame to read */
00731          res = ast_waitfor(chan, timeout);
00732          if (res < 0) {
00733             ast_log(LOG_DEBUG, "ast_waitfor returned <= 0 on chan %s\n", chan->name);
00734             break;
00735          }
00736          if (res == 0)
00737             continue;
00738          f = ast_read(chan);
00739          if (!f) {
00740             ast_log(LOG_DEBUG, "No frame read on channel %s, going out ...\n", chan->name);
00741             returnstatus = AGI_RESULT_HANGUP;
00742             break;
00743          }
00744          /* is there any other frame we should care about
00745             besides AST_CONTROL_HANGUP? */
00746          if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
00747             ast_log(LOG_DEBUG, "Got HANGUP frame on channel %s, going out ...\n", chan->name);
00748             ast_frfree(f);
00749             break;
00750          }
00751          ast_frfree(f);
00752       }
00753    }
00754 
00755    if (async_agi.speech) {
00756       ast_speech_destroy(async_agi.speech);
00757    }
00758 quit:
00759    /* notify manager users this channel cannot be
00760       controlled anymore by Async AGI */
00761    manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: End\r\nChannel: %s\r\n", chan->name);
00762 
00763    /* close the pipe */
00764    close(fds[0]);
00765    close(fds[1]);
00766 
00767    /* intentionally don't get rid of the datastore. So commands can be
00768       still in the queue in case AsyncAGI gets called again.
00769       Datastore destructor will be called on channel destroy anyway  */
00770 
00771    return returnstatus;
00772 
00773 #undef AGI_BUF_SIZE
00774 #undef AMI_BUF_SIZE
00775 }
00776 
00777 /* launch_netscript: The fastagi handler.
00778    FastAGI defaults to port 4573 */
00779 static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
00780 {
00781    int s, flags, res, port = AGI_PORT;
00782    struct pollfd pfds[1];
00783    char *host, *c, *script = "";
00784    struct sockaddr_in addr_in;
00785    struct hostent *hp;
00786    struct ast_hostent ahp;
00787 
00788    /* agiusl is "agi://host.domain[:port][/script/name]" */
00789    host = ast_strdupa(agiurl + 6);  /* Remove agi:// */
00790    /* Strip off any script name */
00791    if ((c = strchr(host, '/'))) {
00792       *c = '\0';
00793       c++;
00794       script = c;
00795    }
00796    if ((c = strchr(host, ':'))) {
00797       *c = '\0';
00798       c++;
00799       port = atoi(c);
00800    }
00801    if (efd) {
00802       ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
00803       return -1;
00804    }
00805    if (!(hp = ast_gethostbyname(host, &ahp))) {
00806       ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
00807       return -1;
00808    }
00809    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
00810       ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
00811       return -1;
00812    }
00813    if ((flags = fcntl(s, F_GETFL)) < 0) {
00814       ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
00815       close(s);
00816       return -1;
00817    }
00818    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
00819       ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
00820       close(s);
00821       return -1;
00822    }
00823    memset(&addr_in, 0, sizeof(addr_in));
00824    addr_in.sin_family = AF_INET;
00825    addr_in.sin_port = htons(port);
00826    memcpy(&addr_in.sin_addr, hp->h_addr, sizeof(addr_in.sin_addr));
00827    if (connect(s, (struct sockaddr *)&addr_in, sizeof(addr_in)) && (errno != EINPROGRESS)) {
00828       ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
00829       close(s);
00830       return AGI_RESULT_FAILURE;
00831    }
00832 
00833    pfds[0].fd = s;
00834    pfds[0].events = POLLOUT;
00835    while ((res = ast_poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
00836       if (errno != EINTR) {
00837          if (!res) {
00838             ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
00839                agiurl, MAX_AGI_CONNECT);
00840          } else
00841             ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00842          close(s);
00843          return AGI_RESULT_FAILURE;
00844       }
00845    }
00846 
00847    if (ast_agi_send(s, NULL, "agi_network: yes\n") < 0) {
00848       if (errno != EINTR) {
00849          ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00850          close(s);
00851          return AGI_RESULT_FAILURE;
00852       }
00853    }
00854 
00855    /* If we have a script parameter, relay it to the fastagi server */
00856    /* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */
00857    if (!ast_strlen_zero(script))
00858       ast_agi_send(s, NULL, "agi_network_script: %s\n", script);
00859 
00860    ast_debug(4, "Wow, connected!\n");
00861    fds[0] = s;
00862    fds[1] = s;
00863    *opid = -1;
00864    return AGI_RESULT_SUCCESS_FAST;
00865 }
00866 
00867 static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
00868 {
00869    char tmp[256];
00870    int pid, toast[2], fromast[2], audio[2], res;
00871    struct stat st;
00872 
00873    if (!strncasecmp(script, "agi://", 6))
00874       return launch_netscript(script, argv, fds, efd, opid);
00875    if (!strncasecmp(script, "agi:async", sizeof("agi:async")-1))
00876       return launch_asyncagi(chan, argv, efd);
00877 
00878    if (script[0] != '/') {
00879       snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_AGI_DIR, script);
00880       script = tmp;
00881    }
00882 
00883    /* Before even trying let's see if the file actually exists */
00884    if (stat(script, &st)) {
00885       ast_log(LOG_WARNING, "Failed to execute '%s': File does not exist.\n", script);
00886       return AGI_RESULT_NOTFOUND;
00887    }
00888 
00889    if (pipe(toast)) {
00890       ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
00891       return AGI_RESULT_FAILURE;
00892    }
00893    if (pipe(fromast)) {
00894       ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
00895       close(toast[0]);
00896       close(toast[1]);
00897       return AGI_RESULT_FAILURE;
00898    }
00899    if (efd) {
00900       if (pipe(audio)) {
00901          ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
00902          close(fromast[0]);
00903          close(fromast[1]);
00904          close(toast[0]);
00905          close(toast[1]);
00906          return AGI_RESULT_FAILURE;
00907       }
00908       res = fcntl(audio[1], F_GETFL);
00909       if (res > -1)
00910          res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
00911       if (res < 0) {
00912          ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
00913          close(fromast[0]);
00914          close(fromast[1]);
00915          close(toast[0]);
00916          close(toast[1]);
00917          close(audio[0]);
00918          close(audio[1]);
00919          return AGI_RESULT_FAILURE;
00920       }
00921    }
00922 
00923    if ((pid = ast_safe_fork(1)) < 0) {
00924       ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00925       return AGI_RESULT_FAILURE;
00926    }
00927    if (!pid) {
00928       /* Pass paths to AGI via environmental variables */
00929       setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1);
00930       setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1);
00931       setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1);
00932       setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1);
00933       setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1);
00934       setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1);
00935       setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1);
00936       setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1);
00937       setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1);
00938       setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1);
00939       setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1);
00940 
00941       /* Don't run AGI scripts with realtime priority -- it causes audio stutter */
00942       ast_set_priority(0);
00943 
00944       /* Redirect stdin and out, provide enhanced audio channel if desired */
00945       dup2(fromast[0], STDIN_FILENO);
00946       dup2(toast[1], STDOUT_FILENO);
00947       if (efd)
00948          dup2(audio[0], STDERR_FILENO + 1);
00949       else
00950          close(STDERR_FILENO + 1);
00951 
00952       /* Close everything but stdin/out/error */
00953       ast_close_fds_above_n(STDERR_FILENO + 1);
00954 
00955       /* Execute script */
00956       /* XXX argv should be deprecated in favor of passing agi_argX paramaters */
00957       execv(script, argv);
00958       /* Can't use ast_log since FD's are closed */
00959       ast_child_verbose(1, "Failed to execute '%s': %s", script, strerror(errno));
00960       /* Special case to set status of AGI to failure */
00961       fprintf(stdout, "failure\n");
00962       fflush(stdout);
00963       _exit(1);
00964    }
00965    ast_verb(3, "Launched AGI Script %s\n", script);
00966    fds[0] = toast[0];
00967    fds[1] = fromast[1];
00968    if (efd)
00969       *efd = audio[1];
00970    /* close what we're not using in the parent */
00971    close(toast[1]);
00972    close(fromast[0]);
00973 
00974    if (efd)
00975       close(audio[0]);
00976 
00977    *opid = pid;
00978    return AGI_RESULT_SUCCESS;
00979 }
00980 
00981 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[])
00982 {
00983    int count;
00984 
00985    /* Print initial environment, with agi_request always being the first
00986       thing */
00987    ast_agi_send(fd, chan, "agi_request: %s\n", request);
00988    ast_agi_send(fd, chan, "agi_channel: %s\n", chan->name);
00989    ast_agi_send(fd, chan, "agi_language: %s\n", chan->language);
00990    ast_agi_send(fd, chan, "agi_type: %s\n", chan->tech->type);
00991    ast_agi_send(fd, chan, "agi_uniqueid: %s\n", chan->uniqueid);
00992    ast_agi_send(fd, chan, "agi_version: %s\n", ast_get_version());
00993 
00994    /* ANI/DNIS */
00995    ast_agi_send(fd, chan, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown"));
00996    ast_agi_send(fd, chan, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown"));
00997    ast_agi_send(fd, chan, "agi_callingpres: %d\n", chan->cid.cid_pres);
00998    ast_agi_send(fd, chan, "agi_callingani2: %d\n", chan->cid.cid_ani2);
00999    ast_agi_send(fd, chan, "agi_callington: %d\n", chan->cid.cid_ton);
01000    ast_agi_send(fd, chan, "agi_callingtns: %d\n", chan->cid.cid_tns);
01001    ast_agi_send(fd, chan, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown"));
01002    ast_agi_send(fd, chan, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown"));
01003 
01004    /* Context information */
01005    ast_agi_send(fd, chan, "agi_context: %s\n", chan->context);
01006    ast_agi_send(fd, chan, "agi_extension: %s\n", chan->exten);
01007    ast_agi_send(fd, chan, "agi_priority: %d\n", chan->priority);
01008    ast_agi_send(fd, chan, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
01009 
01010    /* User information */
01011    ast_agi_send(fd, chan, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
01012    ast_agi_send(fd, chan, "agi_threadid: %ld\n", (long)pthread_self());
01013 
01014    /* Send any parameters to the fastagi server that have been passed via the agi application */
01015    /* Agi application paramaters take the form of: AGI(/path/to/example/script|${EXTEN}) */
01016    for(count = 1; count < argc; count++)
01017       ast_agi_send(fd, chan, "agi_arg_%d: %s\n", count, argv[count]);
01018 
01019    /* End with empty return */
01020    ast_agi_send(fd, chan, "\n");
01021 }
01022 
01023 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01024 {
01025    int res = 0;
01026 
01027    /* Answer the channel */
01028    if (chan->_state != AST_STATE_UP)
01029       res = ast_answer(chan);
01030 
01031    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01032    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01033 }
01034 
01035 static int handle_asyncagi_break(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01036 {
01037    ast_agi_send(agi->fd, chan, "200 result=0\n");
01038    return RESULT_FAILURE;
01039 }
01040 
01041 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01042 {
01043    int res, to;
01044 
01045    if (argc != 4)
01046       return RESULT_SHOWUSAGE;
01047    if (sscanf(argv[3], "%30d", &to) != 1)
01048       return RESULT_SHOWUSAGE;
01049    res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
01050    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01051    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01052 }
01053 
01054 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01055 {
01056    int res;
01057 
01058    if (argc != 3)
01059       return RESULT_SHOWUSAGE;
01060 
01061    /* At the moment, the parser (perhaps broken) returns with
01062       the last argument PLUS the newline at the end of the input
01063       buffer. This probably needs to be fixed, but I wont do that
01064       because other stuff may break as a result. The right way
01065       would probably be to strip off the trailing newline before
01066       parsing, then here, add a newline at the end of the string
01067       before sending it to ast_sendtext --DUDE */
01068    res = ast_sendtext(chan, argv[2]);
01069    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01070    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01071 }
01072 
01073 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01074 {
01075    int res;
01076 
01077    if (argc != 3)
01078       return RESULT_SHOWUSAGE;
01079 
01080    res = ast_recvchar(chan,atoi(argv[2]));
01081    if (res == 0) {
01082       ast_agi_send(agi->fd, chan, "200 result=%d (timeout)\n", res);
01083       return RESULT_SUCCESS;
01084    }
01085    if (res > 0) {
01086       ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01087       return RESULT_SUCCESS;
01088    }
01089    ast_agi_send(agi->fd, chan, "200 result=%d (hangup)\n", res);
01090    return RESULT_FAILURE;
01091 }
01092 
01093 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01094 {
01095    char *buf;
01096 
01097    if (argc != 3)
01098       return RESULT_SHOWUSAGE;
01099 
01100    buf = ast_recvtext(chan, atoi(argv[2]));
01101    if (buf) {
01102       ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", buf);
01103       ast_free(buf);
01104    } else {
01105       ast_agi_send(agi->fd, chan, "200 result=-1\n");
01106    }
01107    return RESULT_SUCCESS;
01108 }
01109 
01110 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01111 {
01112    int res, x;
01113 
01114    if (argc != 3)
01115       return RESULT_SHOWUSAGE;
01116 
01117    if (!strncasecmp(argv[2],"on",2)) {
01118       x = 1;
01119    } else  {
01120       x = 0;
01121    }
01122    if (!strncasecmp(argv[2],"mate",4))  {
01123       x = 2;
01124    }
01125    if (!strncasecmp(argv[2],"tdd",3)) {
01126       x = 1;
01127    }
01128    res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
01129    if (res != RESULT_SUCCESS) {
01130       ast_agi_send(agi->fd, chan, "200 result=0\n");
01131    } else {
01132       ast_agi_send(agi->fd, chan, "200 result=1\n");
01133    }
01134    return RESULT_SUCCESS;
01135 }
01136 
01137 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01138 {
01139    int res;
01140 
01141    if (argc != 3) {
01142       return RESULT_SHOWUSAGE;
01143    }
01144 
01145    res = ast_send_image(chan, argv[2]);
01146    if (!ast_check_hangup(chan)) {
01147       res = 0;
01148    }
01149    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01150    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01151 }
01152 
01153 static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01154 {
01155    int res = 0, skipms = 3000;
01156    char *fwd = "#", *rev = "*", *suspend = NULL, *stop = NULL; /* Default values */
01157 
01158    if (argc < 5 || argc > 9) {
01159       return RESULT_SHOWUSAGE;
01160    }
01161 
01162    if (!ast_strlen_zero(argv[4])) {
01163       stop = argv[4];
01164    }
01165 
01166    if ((argc > 5) && (sscanf(argv[5], "%30d", &skipms) != 1)) {
01167       return RESULT_SHOWUSAGE;
01168    }
01169 
01170    if (argc > 6 && !ast_strlen_zero(argv[6])) {
01171       fwd = argv[6];
01172    }
01173 
01174    if (argc > 7 && !ast_strlen_zero(argv[7])) {
01175       rev = argv[7];
01176    }
01177 
01178    if (argc > 8 && !ast_strlen_zero(argv[8])) {
01179       suspend = argv[8];
01180    }
01181 
01182    res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, suspend, NULL, skipms, NULL);
01183 
01184    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01185 
01186    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01187 }
01188 
01189 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01190 {
01191    int res, vres;
01192    struct ast_filestream *fs, *vfs;
01193    long sample_offset = 0, max_length;
01194    char *edigits = "";
01195 
01196    if (argc < 4 || argc > 5)
01197       return RESULT_SHOWUSAGE;
01198 
01199    if (argv[3])
01200       edigits = argv[3];
01201 
01202    if ((argc > 4) && (sscanf(argv[4], "%30ld", &sample_offset) != 1))
01203       return RESULT_SHOWUSAGE;
01204 
01205    if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
01206       ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
01207       return RESULT_SUCCESS;
01208    }
01209 
01210    if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
01211       ast_debug(1, "Ooh, found a video stream, too\n");
01212 
01213    ast_verb(3, "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
01214 
01215    ast_seekstream(fs, 0, SEEK_END);
01216    max_length = ast_tellstream(fs);
01217    ast_seekstream(fs, sample_offset, SEEK_SET);
01218    res = ast_applystream(chan, fs);
01219    if (vfs)
01220       vres = ast_applystream(chan, vfs);
01221    ast_playstream(fs);
01222    if (vfs)
01223       ast_playstream(vfs);
01224 
01225    res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
01226    /* this is to check for if ast_waitstream closed the stream, we probably are at
01227     * the end of the stream, return that amount, else check for the amount */
01228    sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
01229    ast_stopstream(chan);
01230    if (res == 1) {
01231       /* Stop this command, don't print a result line, as there is a new command */
01232       return RESULT_SUCCESS;
01233    }
01234    ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset);
01235    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01236 }
01237 
01238 /*! \brief get option - really similar to the handle_streamfile, but with a timeout */
01239 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01240 {
01241    int res, vres;
01242    struct ast_filestream *fs, *vfs;
01243    long sample_offset = 0, max_length;
01244    int timeout = 0;
01245    char *edigits = "";
01246 
01247    if ( argc < 4 || argc > 5 )
01248       return RESULT_SHOWUSAGE;
01249 
01250    if ( argv[3] )
01251       edigits = argv[3];
01252 
01253    if ( argc == 5 )
01254       timeout = atoi(argv[4]);
01255    else if (chan->pbx->dtimeoutms) {
01256       /* by default dtimeout is set to 5sec */
01257       timeout = chan->pbx->dtimeoutms; /* in msec */
01258    }
01259 
01260    if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
01261       ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
01262       ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
01263       return RESULT_SUCCESS;
01264    }
01265 
01266    if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
01267       ast_debug(1, "Ooh, found a video stream, too\n");
01268 
01269    ast_verb(3, "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
01270 
01271    ast_seekstream(fs, 0, SEEK_END);
01272    max_length = ast_tellstream(fs);
01273    ast_seekstream(fs, sample_offset, SEEK_SET);
01274    res = ast_applystream(chan, fs);
01275    if (vfs)
01276       vres = ast_applystream(chan, vfs);
01277    ast_playstream(fs);
01278    if (vfs)
01279       ast_playstream(vfs);
01280 
01281    res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
01282    /* this is to check for if ast_waitstream closed the stream, we probably are at
01283     * the end of the stream, return that amount, else check for the amount */
01284    sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
01285    ast_stopstream(chan);
01286    if (res == 1) {
01287       /* Stop this command, don't print a result line, as there is a new command */
01288       return RESULT_SUCCESS;
01289    }
01290 
01291    /* If the user didnt press a key, wait for digitTimeout*/
01292    if (res == 0 ) {
01293       res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
01294       /* Make sure the new result is in the escape digits of the GET OPTION */
01295       if ( !strchr(edigits,res) )
01296          res=0;
01297    }
01298 
01299    ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset);
01300    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01301 }
01302 
01303 
01304 
01305 
01306 /*! \brief Say number in various language syntaxes */
01307 /* While waiting, we're sending a NULL.  */
01308 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01309 {
01310    int res, num;
01311 
01312    if (argc < 4 || argc > 5)
01313       return RESULT_SHOWUSAGE;
01314    if (sscanf(argv[2], "%30d", &num) != 1)
01315       return RESULT_SHOWUSAGE;
01316    res = ast_say_number_full(chan, num, argv[3], chan->language, argc > 4 ? argv[4] : NULL, agi->audio, agi->ctrl);
01317    if (res == 1)
01318       return RESULT_SUCCESS;
01319    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01320    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01321 }
01322 
01323 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01324 {
01325    int res, num;
01326 
01327    if (argc != 4)
01328       return RESULT_SHOWUSAGE;
01329    if (sscanf(argv[2], "%30d", &num) != 1)
01330       return RESULT_SHOWUSAGE;
01331 
01332    res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01333    if (res == 1) /* New command */
01334       return RESULT_SUCCESS;
01335    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01336    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01337 }
01338 
01339 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01340 {
01341    int res;
01342 
01343    if (argc != 4)
01344       return RESULT_SHOWUSAGE;
01345 
01346    res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01347    if (res == 1) /* New command */
01348       return RESULT_SUCCESS;
01349    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01350    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01351 }
01352 
01353 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01354 {
01355    int res, num;
01356 
01357    if (argc != 4)
01358       return RESULT_SHOWUSAGE;
01359    if (sscanf(argv[2], "%30d", &num) != 1)
01360       return RESULT_SHOWUSAGE;
01361    res = ast_say_date(chan, num, argv[3], chan->language);
01362    if (res == 1)
01363       return RESULT_SUCCESS;
01364    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01365    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01366 }
01367 
01368 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01369 {
01370    int res, num;
01371 
01372    if (argc != 4)
01373       return RESULT_SHOWUSAGE;
01374    if (sscanf(argv[2], "%30d", &num) != 1)
01375       return RESULT_SHOWUSAGE;
01376    res = ast_say_time(chan, num, argv[3], chan->language);
01377    if (res == 1)
01378       return RESULT_SUCCESS;
01379    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01380    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01381 }
01382 
01383 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01384 {
01385    int res = 0;
01386    time_t unixtime;
01387    char *format, *zone = NULL;
01388 
01389    if (argc < 4)
01390       return RESULT_SHOWUSAGE;
01391 
01392    if (argc > 4) {
01393       format = argv[4];
01394    } else {
01395       /* XXX this doesn't belong here, but in the 'say' module */
01396       if (!strcasecmp(chan->language, "de")) {
01397          format = "A dBY HMS";
01398       } else {
01399          format = "ABdY 'digits/at' IMp";
01400       }
01401    }
01402 
01403    if (argc > 5 && !ast_strlen_zero(argv[5]))
01404       zone = argv[5];
01405 
01406    if (ast_get_time_t(argv[2], &unixtime, 0, NULL))
01407       return RESULT_SHOWUSAGE;
01408 
01409    res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone);
01410    if (res == 1)
01411       return RESULT_SUCCESS;
01412 
01413    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01414    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01415 }
01416 
01417 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01418 {
01419    int res;
01420 
01421    if (argc != 4)
01422       return RESULT_SHOWUSAGE;
01423 
01424    res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01425    if (res == 1) /* New command */
01426       return RESULT_SUCCESS;
01427    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01428    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01429 }
01430 
01431 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01432 {
01433    int res, max, timeout;
01434    char data[1024];
01435 
01436    if (argc < 3)
01437       return RESULT_SHOWUSAGE;
01438    if (argc >= 4)
01439       timeout = atoi(argv[3]);
01440    else
01441       timeout = 0;
01442    if (argc >= 5)
01443       max = atoi(argv[4]);
01444    else
01445       max = 1024;
01446    res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
01447    if (res == 2)        /* New command */
01448       return RESULT_SUCCESS;
01449    else if (res == 1)
01450       ast_agi_send(agi->fd, chan, "200 result=%s (timeout)\n", data);
01451    else if (res < 0 )
01452       ast_agi_send(agi->fd, chan, "200 result=-1\n");
01453    else
01454       ast_agi_send(agi->fd, chan, "200 result=%s\n", data);
01455    return RESULT_SUCCESS;
01456 }
01457 
01458 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01459 {
01460 
01461    if (argc != 3)
01462       return RESULT_SHOWUSAGE;
01463    ast_copy_string(chan->context, argv[2], sizeof(chan->context));
01464    ast_agi_send(agi->fd, chan, "200 result=0\n");
01465    return RESULT_SUCCESS;
01466 }
01467 
01468 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01469 {
01470    if (argc != 3)
01471       return RESULT_SHOWUSAGE;
01472    ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
01473    ast_agi_send(agi->fd, chan, "200 result=0\n");
01474    return RESULT_SUCCESS;
01475 }
01476 
01477 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01478 {
01479    int pri;
01480 
01481    if (argc != 3)
01482       return RESULT_SHOWUSAGE;
01483 
01484    if (sscanf(argv[2], "%30d", &pri) != 1) {
01485       if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
01486          return RESULT_SHOWUSAGE;
01487    }
01488 
01489    ast_explicit_goto(chan, NULL, NULL, pri);
01490    ast_agi_send(agi->fd, chan, "200 result=0\n");
01491    return RESULT_SUCCESS;
01492 }
01493 
01494 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01495 {
01496    struct ast_filestream *fs;
01497    struct ast_frame *f;
01498    struct timeval start;
01499    long sample_offset = 0;
01500    int res = 0;
01501    int ms;
01502 
01503    struct ast_dsp *sildet=NULL;         /* silence detector dsp */
01504    int totalsilence = 0;
01505    int dspsilence = 0;
01506    int silence = 0;                /* amount of silence to allow */
01507    int gotsilence = 0;             /* did we timeout for silence? */
01508    char *silencestr = NULL;
01509    int rfmt = 0;
01510 
01511    /* XXX EAGI FIXME XXX */
01512 
01513    if (argc < 6)
01514       return RESULT_SHOWUSAGE;
01515    if (sscanf(argv[5], "%30d", &ms) != 1)
01516       return RESULT_SHOWUSAGE;
01517 
01518    if (argc > 6)
01519       silencestr = strchr(argv[6],'s');
01520    if ((argc > 7) && (!silencestr))
01521       silencestr = strchr(argv[7],'s');
01522    if ((argc > 8) && (!silencestr))
01523       silencestr = strchr(argv[8],'s');
01524 
01525    if (silencestr) {
01526       if (strlen(silencestr) > 2) {
01527          if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
01528             silencestr++;
01529             silencestr++;
01530             if (silencestr)
01531                silence = atoi(silencestr);
01532             if (silence > 0)
01533                silence *= 1000;
01534          }
01535       }
01536    }
01537 
01538    if (silence > 0) {
01539       rfmt = chan->readformat;
01540       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
01541       if (res < 0) {
01542          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
01543          return -1;
01544       }
01545       sildet = ast_dsp_new();
01546       if (!sildet) {
01547          ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
01548          return -1;
01549       }
01550       ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
01551    }
01552    
01553    /* backward compatibility, if no offset given, arg[6] would have been
01554     * caught below and taken to be a beep, else if it is a digit then it is a
01555     * offset */
01556    if ((argc >6) && (sscanf(argv[6], "%30ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
01557       res = ast_streamfile(chan, "beep", chan->language);
01558 
01559    if ((argc > 7) && (!strchr(argv[7], '=')))
01560       res = ast_streamfile(chan, "beep", chan->language);
01561 
01562    if (!res)
01563       res = ast_waitstream(chan, argv[4]);
01564    if (res) {
01565       ast_agi_send(agi->fd, chan, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
01566    } else {
01567       fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, AST_FILE_MODE);
01568       if (!fs) {
01569          res = -1;
01570          ast_agi_send(agi->fd, chan, "200 result=%d (writefile)\n", res);
01571          if (sildet)
01572             ast_dsp_free(sildet);
01573          return RESULT_FAILURE;
01574       }
01575 
01576       /* Request a video update */
01577       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
01578 
01579       chan->stream = fs;
01580       ast_applystream(chan,fs);
01581       /* really should have checks */
01582       ast_seekstream(fs, sample_offset, SEEK_SET);
01583       ast_truncstream(fs);
01584 
01585       start = ast_tvnow();
01586       while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
01587          res = ast_waitfor(chan, ms - ast_tvdiff_ms(ast_tvnow(), start));
01588          if (res < 0) {
01589             ast_closestream(fs);
01590             ast_agi_send(agi->fd, chan, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
01591             if (sildet)
01592                ast_dsp_free(sildet);
01593             return RESULT_FAILURE;
01594          }
01595          f = ast_read(chan);
01596          if (!f) {
01597             ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
01598             ast_closestream(fs);
01599             if (sildet)
01600                ast_dsp_free(sildet);
01601             return RESULT_FAILURE;
01602          }
01603          switch(f->frametype) {
01604          case AST_FRAME_DTMF:
01605             if (strchr(argv[4], f->subclass)) {
01606                /* This is an interrupting chracter, so rewind to chop off any small
01607                   amount of DTMF that may have been recorded
01608                */
01609                ast_stream_rewind(fs, 200);
01610                ast_truncstream(fs);
01611                sample_offset = ast_tellstream(fs);
01612                ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
01613                ast_closestream(fs);
01614                ast_frfree(f);
01615                if (sildet)
01616                   ast_dsp_free(sildet);
01617                return RESULT_SUCCESS;
01618             }
01619             break;
01620          case AST_FRAME_VOICE:
01621             ast_writestream(fs, f);
01622             /* this is a safe place to check progress since we know that fs
01623              * is valid after a write, and it will then have our current
01624              * location */
01625             sample_offset = ast_tellstream(fs);
01626             if (silence > 0) {
01627                dspsilence = 0;
01628                ast_dsp_silence(sildet, f, &dspsilence);
01629                if (dspsilence) {
01630                   totalsilence = dspsilence;
01631                } else {
01632                   totalsilence = 0;
01633                }
01634                if (totalsilence > silence) {
01635                   /* Ended happily with silence */
01636                   gotsilence = 1;
01637                   break;
01638                }
01639             }
01640             break;
01641          case AST_FRAME_VIDEO:
01642             ast_writestream(fs, f);
01643          default:
01644             /* Ignore all other frames */
01645             break;
01646          }
01647          ast_frfree(f);
01648          if (gotsilence)
01649             break;
01650       }
01651 
01652       if (gotsilence) {
01653          ast_stream_rewind(fs, silence-1000);
01654          ast_truncstream(fs);
01655          sample_offset = ast_tellstream(fs);
01656       }
01657       ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
01658       ast_closestream(fs);
01659    }
01660 
01661    if (silence > 0) {
01662       res = ast_set_read_format(chan, rfmt);
01663       if (res)
01664          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
01665       ast_dsp_free(sildet);
01666    }
01667 
01668    return RESULT_SUCCESS;
01669 }
01670 
01671 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01672 {
01673    double timeout;
01674    struct timeval whentohangup = { 0, 0 };
01675 
01676    if (argc != 3)
01677       return RESULT_SHOWUSAGE;
01678    if (sscanf(argv[2], "%30lf", &timeout) != 1)
01679       return RESULT_SHOWUSAGE;
01680    if (timeout < 0)
01681       timeout = 0;
01682    if (timeout) {
01683       whentohangup.tv_sec = timeout;
01684       whentohangup.tv_usec = (timeout - whentohangup.tv_sec) * 1000000.0;
01685    }
01686    ast_channel_setwhentohangup_tv(chan, whentohangup);
01687    ast_agi_send(agi->fd, chan, "200 result=0\n");
01688    return RESULT_SUCCESS;
01689 }
01690 
01691 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01692 {
01693    struct ast_channel *c;
01694 
01695    if (argc == 1) {
01696       /* no argument: hangup the current channel */
01697       ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
01698       ast_agi_send(agi->fd, chan, "200 result=1\n");
01699       return RESULT_SUCCESS;
01700    } else if (argc == 2) {
01701       /* one argument: look for info on the specified channel */
01702       c = ast_get_channel_by_name_locked(argv[1]);
01703       if (c) {
01704          /* we have a matching channel */
01705          ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
01706          ast_agi_send(agi->fd, chan, "200 result=1\n");
01707          ast_channel_unlock(c);
01708          return RESULT_SUCCESS;
01709       }
01710       /* if we get this far no channel name matched the argument given */
01711       ast_agi_send(agi->fd, chan, "200 result=-1\n");
01712       return RESULT_SUCCESS;
01713    } else {
01714       return RESULT_SHOWUSAGE;
01715    }
01716 }
01717 
01718 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01719 {
01720    int res, workaround;
01721    struct ast_app *app_to_exec;
01722 
01723    if (argc < 2)
01724       return RESULT_SHOWUSAGE;
01725 
01726    ast_verb(3, "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argc >= 3 ? argv[2] : "");
01727 
01728    if ((app_to_exec = pbx_findapp(argv[1]))) {
01729       if(!strcasecmp(argv[1], PARK_APP_NAME)) {
01730          ast_masq_park_call(chan, NULL, 0, NULL);
01731       }
01732       if (!(workaround = ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS))) {
01733          ast_set_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
01734       }
01735       if (ast_compat_res_agi && argc >= 3 && !ast_strlen_zero(argv[2])) {
01736          char *compat = alloca(strlen(argv[2]) * 2 + 1), *cptr, *vptr;
01737          for (cptr = compat, vptr = argv[2]; *vptr; vptr++) {
01738             if (*vptr == ',') {
01739                *cptr++ = '\\';
01740                *cptr++ = ',';
01741             } else if (*vptr == '|') {
01742                *cptr++ = ',';
01743             } else {
01744                *cptr++ = *vptr;
01745             }
01746          }
01747          *cptr = '\0';
01748          res = pbx_exec(chan, app_to_exec, compat);
01749       } else {
01750          res = pbx_exec(chan, app_to_exec, argc == 2 ? "" : argv[2]);
01751       }
01752       if (!workaround) {
01753          ast_clear_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
01754       }
01755    } else {
01756       ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01757       res = -2;
01758    }
01759    ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01760 
01761    /* Even though this is wrong, users are depending upon this result. */
01762    return res;
01763 }
01764 
01765 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01766 {
01767    char tmp[256]="";
01768    char *l = NULL, *n = NULL;
01769 
01770    if (argv[2]) {
01771       ast_copy_string(tmp, argv[2], sizeof(tmp));
01772       ast_callerid_parse(tmp, &n, &l);
01773       if (l)
01774          ast_shrink_phone_number(l);
01775       else
01776          l = "";
01777       if (!n)
01778          n = "";
01779       ast_set_callerid(chan, l, n, NULL);
01780    }
01781 
01782    ast_agi_send(agi->fd, chan, "200 result=1\n");
01783    return RESULT_SUCCESS;
01784 }
01785 
01786 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01787 {
01788    struct ast_channel *c;
01789    if (argc == 2) {
01790       /* no argument: supply info on the current channel */
01791       ast_agi_send(agi->fd, chan, "200 result=%d\n", chan->_state);
01792       return RESULT_SUCCESS;
01793    } else if (argc == 3) {
01794       /* one argument: look for info on the specified channel */
01795       c = ast_get_channel_by_name_locked(argv[2]);
01796       if (c) {
01797          ast_agi_send(agi->fd, chan, "200 result=%d\n", c->_state);
01798          ast_channel_unlock(c);
01799          return RESULT_SUCCESS;
01800       }
01801       /* if we get this far no channel name matched the argument given */
01802       ast_agi_send(agi->fd, chan, "200 result=-1\n");
01803       return RESULT_SUCCESS;
01804    } else {
01805       return RESULT_SHOWUSAGE;
01806    }
01807 }
01808 
01809 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01810 {
01811    if (argv[3])
01812       pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01813 
01814    ast_agi_send(agi->fd, chan, "200 result=1\n");
01815    return RESULT_SUCCESS;
01816 }
01817 
01818 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01819 {
01820    char *ret;
01821    char tempstr[1024];
01822 
01823    if (argc != 3)
01824       return RESULT_SHOWUSAGE;
01825 
01826    /* check if we want to execute an ast_custom_function */
01827    if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01828       ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01829    } else {
01830       pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01831    }
01832 
01833    if (ret)
01834       ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", ret);
01835    else
01836       ast_agi_send(agi->fd, chan, "200 result=0\n");
01837 
01838    return RESULT_SUCCESS;
01839 }
01840 
01841 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01842 {
01843    char tmp[4096];
01844    struct ast_channel *chan2=NULL;
01845 
01846    if ((argc != 4) && (argc != 5))
01847       return RESULT_SHOWUSAGE;
01848    if (argc == 5) {
01849       chan2 = ast_get_channel_by_name_locked(argv[4]);
01850    } else {
01851       chan2 = chan;
01852    }
01853    if (chan2) {
01854       pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01855       ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", tmp);
01856    } else {
01857       ast_agi_send(agi->fd, chan, "200 result=0\n");
01858    }
01859    if (chan2 && (chan2 != chan))
01860       ast_channel_unlock(chan2);
01861    return RESULT_SUCCESS;
01862 }
01863 
01864 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01865 {
01866    int level = 0;
01867 
01868    if (argc < 2)
01869       return RESULT_SHOWUSAGE;
01870 
01871    if (argv[2])
01872       sscanf(argv[2], "%30d", &level);
01873 
01874    ast_verb(level, "%s: %s\n", chan->data, argv[1]);
01875 
01876    ast_agi_send(agi->fd, chan, "200 result=1\n");
01877 
01878    return RESULT_SUCCESS;
01879 }
01880 
01881 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01882 {
01883    int res;
01884    struct ast_str *buf;
01885 
01886    if (argc != 4)
01887       return RESULT_SHOWUSAGE;
01888 
01889    if (!(buf = ast_str_create(16))) {
01890       ast_agi_send(agi->fd, chan, "200 result=-1\n");
01891       return RESULT_SUCCESS;
01892    }
01893 
01894    do {
01895       res = ast_db_get(argv[2], argv[3], ast_str_buffer(buf), ast_str_size(buf));
01896       ast_str_update(buf);
01897       if (ast_str_strlen(buf) < ast_str_size(buf) - 1) {
01898          break;
01899       }
01900       if (ast_str_make_space(&buf, ast_str_size(buf) * 2)) {
01901          break;
01902       }
01903    } while (1);
01904    
01905    if (res)
01906       ast_agi_send(agi->fd, chan, "200 result=0\n");
01907    else
01908       ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", ast_str_buffer(buf));
01909 
01910    ast_free(buf);
01911    return RESULT_SUCCESS;
01912 }
01913 
01914 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01915 {
01916    int res;
01917 
01918    if (argc != 5)
01919       return RESULT_SHOWUSAGE;
01920    res = ast_db_put(argv[2], argv[3], argv[4]);
01921    ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01922    return RESULT_SUCCESS;
01923 }
01924 
01925 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01926 {
01927    int res;
01928 
01929    if (argc != 4)
01930       return RESULT_SHOWUSAGE;
01931    res = ast_db_del(argv[2], argv[3]);
01932    ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01933    return RESULT_SUCCESS;
01934 }
01935 
01936 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01937 {
01938    int res;
01939 
01940    if ((argc < 3) || (argc > 4))
01941       return RESULT_SHOWUSAGE;
01942    if (argc == 4)
01943       res = ast_db_deltree(argv[2], argv[3]);
01944    else
01945       res = ast_db_deltree(argv[2], NULL);
01946 
01947    ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01948    return RESULT_SUCCESS;
01949 }
01950 
01951 static char *handle_cli_agi_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01952 {
01953    switch (cmd) {
01954    case CLI_INIT:
01955       e->command = "agi set debug [on|off]";
01956       e->usage =
01957          "Usage: agi set debug [on|off]\n"
01958          "       Enables/disables dumping of AGI transactions for\n"
01959          "       debugging purposes.\n";
01960       return NULL;
01961 
01962    case CLI_GENERATE:
01963       return NULL;
01964    }
01965 
01966    if (a->argc != e->args)
01967       return CLI_SHOWUSAGE;
01968 
01969    if (strncasecmp(a->argv[3], "off", 3) == 0) {
01970       agidebug = 0;
01971    } else if (strncasecmp(a->argv[3], "on", 2) == 0) {
01972       agidebug = 1;
01973    } else {
01974       return CLI_SHOWUSAGE;
01975    }
01976    ast_cli(a->fd, "AGI Debugging %sabled\n", agidebug ? "En" : "Dis");
01977    return CLI_SUCCESS;
01978 }
01979 
01980 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01981 {
01982    ast_agi_send(agi->fd, chan, "200 result=0\n");
01983    return RESULT_SUCCESS;
01984 }
01985 
01986 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01987 {
01988    if (argc < 3) {
01989       return RESULT_SHOWUSAGE;
01990    }
01991    if (!strncasecmp(argv[2], "on", 2))
01992       ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01993    else if (!strncasecmp(argv[2], "off", 3))
01994       ast_moh_stop(chan);
01995    ast_agi_send(agi->fd, chan, "200 result=0\n");
01996    return RESULT_SUCCESS;
01997 }
01998 
01999 static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02000 {
02001    /* If a structure already exists, return an error */
02002         if (agi->speech) {
02003       ast_agi_send(agi->fd, chan, "200 result=0\n");
02004       return RESULT_SUCCESS;
02005    }
02006 
02007    if ((agi->speech = ast_speech_new(argv[2], AST_FORMAT_SLINEAR)))
02008       ast_agi_send(agi->fd, chan, "200 result=1\n");
02009    else
02010       ast_agi_send(agi->fd, chan, "200 result=0\n");
02011 
02012    return RESULT_SUCCESS;
02013 }
02014 
02015 static int handle_speechset(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02016 {
02017    /* Check for minimum arguments */
02018    if (argc != 4)
02019       return RESULT_SHOWUSAGE;
02020 
02021    /* Check to make sure speech structure exists */
02022    if (!agi->speech) {
02023       ast_agi_send(agi->fd, chan, "200 result=0\n");
02024       return RESULT_SUCCESS;
02025    }
02026 
02027    ast_speech_change(agi->speech, argv[2], argv[3]);
02028    ast_agi_send(agi->fd, chan, "200 result=1\n");
02029 
02030    return RESULT_SUCCESS;
02031 }
02032 
02033 static int handle_speechdestroy(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02034 {
02035    if (agi->speech) {
02036       ast_speech_destroy(agi->speech);
02037       agi->speech = NULL;
02038       ast_agi_send(agi->fd, chan, "200 result=1\n");
02039    } else {
02040       ast_agi_send(agi->fd, chan, "200 result=0\n");
02041    }
02042 
02043    return RESULT_SUCCESS;
02044 }
02045 
02046 static int handle_speechloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02047 {
02048    if (argc != 5)
02049       return RESULT_SHOWUSAGE;
02050 
02051    if (!agi->speech) {
02052       ast_agi_send(agi->fd, chan, "200 result=0\n");
02053       return RESULT_SUCCESS;
02054    }
02055 
02056    if (ast_speech_grammar_load(agi->speech, argv[3], argv[4]))
02057       ast_agi_send(agi->fd, chan, "200 result=0\n");
02058    else
02059       ast_agi_send(agi->fd, chan, "200 result=1\n");
02060 
02061    return RESULT_SUCCESS;
02062 }
02063 
02064 static int handle_speechunloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02065 {
02066    if (argc != 4)
02067       return RESULT_SHOWUSAGE;
02068 
02069    if (!agi->speech) {
02070       ast_agi_send(agi->fd, chan, "200 result=0\n");
02071       return RESULT_SUCCESS;
02072    }
02073 
02074    if (ast_speech_grammar_unload(agi->speech, argv[3]))
02075       ast_agi_send(agi->fd, chan, "200 result=0\n");
02076    else
02077       ast_agi_send(agi->fd, chan, "200 result=1\n");
02078 
02079    return RESULT_SUCCESS;
02080 }
02081 
02082 static int handle_speechactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02083 {
02084    if (argc != 4)
02085       return RESULT_SHOWUSAGE;
02086 
02087    if (!agi->speech) {
02088       ast_agi_send(agi->fd, chan, "200 result=0\n");
02089       return RESULT_SUCCESS;
02090    }
02091 
02092    if (ast_speech_grammar_activate(agi->speech, argv[3]))
02093       ast_agi_send(agi->fd, chan, "200 result=0\n");
02094    else
02095       ast_agi_send(agi->fd, chan, "200 result=1\n");
02096 
02097    return RESULT_SUCCESS;
02098 }
02099 
02100 static int handle_speechdeactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02101 {
02102    if (argc != 4)
02103       return RESULT_SHOWUSAGE;
02104 
02105    if (!agi->speech) {
02106       ast_agi_send(agi->fd, chan, "200 result=0\n");
02107       return RESULT_SUCCESS;
02108    }
02109 
02110    if (ast_speech_grammar_deactivate(agi->speech, argv[3]))
02111       ast_agi_send(agi->fd, chan, "200 result=0\n");
02112    else
02113       ast_agi_send(agi->fd, chan, "200 result=1\n");
02114 
02115    return RESULT_SUCCESS;
02116 }
02117 
02118 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang, int offset)
02119 {
02120    struct ast_filestream *fs = NULL;
02121 
02122    if (!(fs = ast_openstream(chan, filename, preflang)))
02123       return -1;
02124 
02125    if (offset)
02126       ast_seekstream(fs, offset, SEEK_SET);
02127 
02128    if (ast_applystream(chan, fs))
02129       return -1;
02130 
02131    if (ast_playstream(fs))
02132       return -1;
02133 
02134    return 0;
02135 }
02136 
02137 static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc, char **argv)
02138 {
02139    struct ast_speech *speech = agi->speech;
02140    char *prompt, dtmf = 0, tmp[4096] = "", *buf = tmp;
02141    int timeout = 0, offset = 0, old_read_format = 0, res = 0, i = 0;
02142    long current_offset = 0;
02143    const char *reason = NULL;
02144    struct ast_frame *fr = NULL;
02145    struct ast_speech_result *result = NULL;
02146    size_t left = sizeof(tmp);
02147    time_t start = 0, current;
02148 
02149    if (argc < 4)
02150       return RESULT_SHOWUSAGE;
02151 
02152    if (!speech) {
02153       ast_agi_send(agi->fd, chan, "200 result=0\n");
02154       return RESULT_SUCCESS;
02155    }
02156 
02157    prompt = argv[2];
02158    timeout = atoi(argv[3]);
02159 
02160    /* If offset is specified then convert from text to integer */
02161    if (argc == 5)
02162       offset = atoi(argv[4]);
02163 
02164    /* We want frames coming in signed linear */
02165    old_read_format = chan->readformat;
02166    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
02167       ast_agi_send(agi->fd, chan, "200 result=0\n");
02168       return RESULT_SUCCESS;
02169    }
02170 
02171    /* Setup speech structure */
02172    if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
02173       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
02174       ast_speech_start(speech);
02175    }
02176 
02177    /* Start playing prompt */
02178    speech_streamfile(chan, prompt, chan->language, offset);
02179 
02180    /* Go into loop reading in frames, passing to speech thingy, checking for hangup, all that jazz */
02181    while (ast_strlen_zero(reason)) {
02182       /* Run scheduled items */
02183                 ast_sched_runq(chan->sched);
02184 
02185       /* See maximum time of waiting */
02186       if ((res = ast_sched_wait(chan->sched)) < 0)
02187          res = 1000;
02188 
02189       /* Wait for frame */
02190       if (ast_waitfor(chan, res) > 0) {
02191          if (!(fr = ast_read(chan))) {
02192             reason = "hangup";
02193             break;
02194          }
02195       }
02196 
02197       /* Perform timeout check */
02198       if ((timeout > 0) && (start > 0)) {
02199          time(&current);
02200          if ((current - start) >= timeout) {
02201             reason = "timeout";
02202             if (fr)
02203                ast_frfree(fr);
02204             break;
02205          }
02206       }
02207 
02208       /* Check the speech structure for any changes */
02209       ast_mutex_lock(&speech->lock);
02210 
02211       /* See if we need to quiet the audio stream playback */
02212       if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream) {
02213          current_offset = ast_tellstream(chan->stream);
02214          ast_stopstream(chan);
02215          ast_clear_flag(speech, AST_SPEECH_QUIET);
02216       }
02217 
02218       /* Check each state */
02219       switch (speech->state) {
02220       case AST_SPEECH_STATE_READY:
02221          /* If the stream is done, start timeout calculation */
02222          if ((timeout > 0) && start == 0 && ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL))) {
02223             ast_stopstream(chan);
02224             time(&start);
02225          }
02226          /* Write audio frame data into speech engine if possible */
02227          if (fr && fr->frametype == AST_FRAME_VOICE)
02228             ast_speech_write(speech, fr->data.ptr, fr->datalen);
02229          break;
02230       case AST_SPEECH_STATE_WAIT:
02231          /* Cue waiting sound if not already playing */
02232          if ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL)) {
02233             ast_stopstream(chan);
02234             /* If a processing sound exists, or is not none - play it */
02235             if (!ast_strlen_zero(speech->processing_sound) && strcasecmp(speech->processing_sound, "none"))
02236                speech_streamfile(chan, speech->processing_sound, chan->language, 0);
02237          }
02238          break;
02239       case AST_SPEECH_STATE_DONE:
02240          /* Get the results */
02241          speech->results = ast_speech_results_get(speech);
02242          /* Change state to not ready */
02243          ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
02244          reason = "speech";
02245          break;
02246       default:
02247          break;
02248       }
02249       ast_mutex_unlock(&speech->lock);
02250 
02251       /* Check frame for DTMF or hangup */
02252       if (fr) {
02253          if (fr->frametype == AST_FRAME_DTMF) {
02254             reason = "dtmf";
02255             dtmf = fr->subclass;
02256          } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_HANGUP) {
02257             reason = "hangup";
02258          }
02259          ast_frfree(fr);
02260       }
02261    }
02262 
02263    if (!strcasecmp(reason, "speech")) {
02264       /* Build string containing speech results */
02265                 for (result = speech->results; result; result = AST_LIST_NEXT(result, list)) {
02266          /* Build result string */
02267          ast_build_string(&buf, &left, "%sscore%d=%d text%d=\"%s\" grammar%d=%s", (i > 0 ? " " : ""), i, result->score, i, result->text, i, result->grammar);
02268                         /* Increment result count */
02269          i++;
02270       }
02271                 /* Print out */
02272       ast_agi_send(agi->fd, chan, "200 result=1 (speech) endpos=%ld results=%d %s\n", current_offset, i, tmp);
02273    } else if (!strcasecmp(reason, "dtmf")) {
02274       ast_agi_send(agi->fd, chan, "200 result=1 (digit) digit=%c endpos=%ld\n", dtmf, current_offset);
02275    } else if (!strcasecmp(reason, "hangup") || !strcasecmp(reason, "timeout")) {
02276       ast_agi_send(agi->fd, chan, "200 result=1 (%s) endpos=%ld\n", reason, current_offset);
02277    } else {
02278       ast_agi_send(agi->fd, chan, "200 result=0 endpos=%ld\n", current_offset);
02279    }
02280 
02281    return RESULT_SUCCESS;
02282 }
02283 
02284 static char usage_verbose[] =
02285 " Usage: VERBOSE <message> <level>\n"
02286 "  Sends <message> to the console via verbose message system.\n"
02287 " <level> is the the verbose level (1-4)\n"
02288 " Always returns 1.\n";
02289 
02290 static char usage_setvariable[] =
02291 " Usage: SET VARIABLE <variablename> <value>\n";
02292 
02293 static char usage_setcallerid[] =
02294 " Usage: SET CALLERID <number>\n"
02295 "  Changes the callerid of the current channel.\n";
02296 
02297 static char usage_waitfordigit[] =
02298 " Usage: WAIT FOR DIGIT <timeout>\n"
02299 "  Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
02300 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
02301 " the numerical value of the ascii of the digit if one is received.  Use -1\n"
02302 " for the timeout value if you desire the call to block indefinitely.\n";
02303 
02304 static char usage_sendtext[] =
02305 " Usage: SEND TEXT \"<text to send>\"\n"
02306 "  Sends the given text on a channel. Most channels do not support the\n"
02307 " transmission of text.  Returns 0 if text is sent, or if the channel does not\n"
02308 " support text transmission.  Returns -1 only on error/hangup.  Text\n"
02309 " consisting of greater than one word should be placed in quotes since the\n"
02310 " command only accepts a single argument.\n";
02311 
02312 static char usage_recvchar[] =
02313 " Usage: RECEIVE CHAR <timeout>\n"
02314 "  Receives a character of text on a channel. Specify timeout to be the\n"
02315 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02316 " do not support the reception of text. Returns the decimal value of the character\n"
02317 " if one is received, or 0 if the channel does not support text reception.  Returns\n"
02318 " -1 only on error/hangup.\n";
02319 
02320 static char usage_recvtext[] =
02321 " Usage: RECEIVE TEXT <timeout>\n"
02322 "  Receives a string of text on a channel. Specify timeout to be the\n"
02323 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02324 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
02325 
02326 static char usage_tddmode[] =
02327 " Usage: TDD MODE <on|off>\n"
02328 "  Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
02329 " successful, or 0 if channel is not TDD-capable.\n";
02330 
02331 static char usage_sendimage[] =
02332 " Usage: SEND IMAGE <image>\n"
02333 "  Sends the given image on a channel. Most channels do not support the\n"
02334 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
02335 " support image transmission.  Returns -1 only on error/hangup. Image names\n"
02336 " should not include extensions.\n";
02337 
02338 static char usage_streamfile[] =
02339 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
02340 "  Send the given file, allowing playback to be interrupted by the given\n"
02341 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02342 " permitted. If sample offset is provided then the audio will seek to sample\n"
02343 " offset before play starts.  Returns 0 if playback completes without a digit\n"
02344 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02345 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02346 " extension must not be included in the filename.\n";
02347 
02348 static char usage_controlstreamfile[] =
02349 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
02350 "  Send the given file, allowing playback to be controled by the given\n"
02351 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02352 " permitted.  Returns 0 if playback completes without a digit\n"
02353 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02354 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02355 " extension must not be included in the filename.\n\n"
02356 " Note: ffchar and rewchar default to * and # respectively.\n";
02357 
02358 static char usage_saynumber[] =
02359 " Usage: SAY NUMBER <number> <escape digits> [gender]\n"
02360 "  Say a given number, returning early if any of the given DTMF digits\n"
02361 " are received on the channel.  Returns 0 if playback completes without a digit\n"
02362 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02363 " -1 on error/hangup.\n";
02364 
02365 static char usage_saydigits[] =
02366 " Usage: SAY DIGITS <number> <escape digits>\n"
02367 "  Say a given digit string, returning early if any of the given DTMF digits\n"
02368 " are received on the channel. Returns 0 if playback completes without a digit\n"
02369 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02370 " -1 on error/hangup.\n";
02371 
02372 static char usage_sayalpha[] =
02373 " Usage: SAY ALPHA <number> <escape digits>\n"
02374 "  Say a given character string, returning early if any of the given DTMF digits\n"
02375 " are received on the channel. Returns 0 if playback completes without a digit\n"
02376 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02377 " -1 on error/hangup.\n";
02378 
02379 static char usage_saydate[] =
02380 " Usage: SAY DATE <date> <escape digits>\n"
02381 "  Say a given date, returning early if any of the given DTMF digits are\n"
02382 " received on the channel.  <date> is number of seconds elapsed since 00:00:00\n"
02383 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02384 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02385 " digit if one was pressed or -1 on error/hangup.\n";
02386 
02387 static char usage_saytime[] =
02388 " Usage: SAY TIME <time> <escape digits>\n"
02389 "  Say a given time, returning early if any of the given DTMF digits are\n"
02390 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
02391 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02392 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02393 " digit if one was pressed or -1 on error/hangup.\n";
02394 
02395 static char usage_saydatetime[] =
02396 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
02397 "  Say a given time, returning early if any of the given DTMF digits are\n"
02398 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
02399 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
02400 " the time should be said in.  See voicemail.conf (defaults to \"ABdY\n"
02401 " 'digits/at' IMp\").  Acceptable values for [timezone] can be found in\n"
02402 " /usr/share/zoneinfo.  Defaults to machine default. Returns 0 if playback\n"
02403 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02404 " digit if one was pressed or -1 on error/hangup.\n";
02405 
02406 static char usage_sayphonetic[] =
02407 " Usage: SAY PHONETIC <string> <escape digits>\n"
02408 "  Say a given character string with phonetics, returning early if any of the\n"
02409 " given DTMF digits are received on the channel. Returns 0 if playback\n"
02410 " completes without a digit pressed, the ASCII numerical value of the digit\n"
02411 " if one was pressed, or -1 on error/hangup.\n";
02412 
02413 static char usage_setcontext[] =
02414 " Usage: SET CONTEXT <desired context>\n"
02415 "  Sets the context for continuation upon exiting the application.\n";
02416 
02417 static char usage_setextension[] =
02418 " Usage: SET EXTENSION <new extension>\n"
02419 "  Changes the extension for continuation upon exiting the application.\n";
02420 
02421 static char usage_setpriority[] =
02422 " Usage: SET PRIORITY <priority>\n"
02423 "  Changes the priority for continuation upon exiting the application.\n"
02424 " The priority must be a valid priority or label.\n";
02425 
02426 static char usage_recordfile[] =
02427 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
02428 "                                          [offset samples] [BEEP] [s=silence]\n"
02429 "  Record to a file until a given dtmf digit in the sequence is received\n"
02430 " Returns -1 on hangup or error.  The format will specify what kind of file\n"
02431 " will be recorded.  The timeout is the maximum record time in milliseconds, or\n"
02432 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
02433 " to the offset without exceeding the end of the file.  \"silence\" is the number\n"
02434 " of seconds of silence allowed before the function returns despite the\n"
02435 " lack of dtmf digits or reaching timeout.  Silence value must be\n"
02436 " preceeded by \"s=\" and is also optional.\n";
02437 
02438 static char usage_autohangup[] =
02439 " Usage: SET AUTOHANGUP <time>\n"
02440 "  Cause the channel to automatically hangup at <time> seconds in the\n"
02441 " future.  Of course it can be hungup before then as well. Setting to 0 will\n"
02442 " cause the autohangup feature to be disabled on this channel.\n";
02443 
02444 static char usage_speechcreate[] =
02445 " Usage: SPEECH CREATE <engine>\n"
02446 "       Create a speech object to be used by the other Speech AGI commands.\n";
02447 
02448 static char usage_speechset[] =
02449 " Usage: SPEECH SET <name> <value>\n"
02450 "       Set an engine-specific setting.\n";
02451 
02452 static char usage_speechdestroy[] =
02453 " Usage: SPEECH DESTROY\n"
02454 "       Destroy the speech object created by SPEECH CREATE.\n";
02455 
02456 static char usage_speechloadgrammar[] =
02457 " Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar>\n"
02458 "       Loads the specified grammar as the specified name.\n";
02459 
02460 static char usage_speechunloadgrammar[] =
02461 " Usage: SPEECH UNLOAD GRAMMAR <grammar name>\n"
02462 "       Unloads the specified grammar.\n";
02463 
02464 static char usage_speechactivategrammar[] =
02465 " Usage: SPEECH ACTIVATE GRAMMAR <grammar name>\n"
02466 "       Activates the specified grammar on the speech object.\n";
02467 
02468 static char usage_speechdeactivategrammar[] =
02469 " Usage: SPEECH DEACTIVATE GRAMMAR <grammar name>\n"
02470 "       Deactivates the specified grammar on the speech object.\n";
02471 
02472 static char usage_speechrecognize[] =
02473 " Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>]\n"
02474 "       Plays back given prompt while listening for speech and dtmf.\n";
02475 
02476 /*!
02477  * \brief AGI commands list
02478  */
02479 static struct agi_command commands[] = {
02480    { { "answer", NULL }, handle_answer, NULL, NULL, 0 },
02481    { { "asyncagi", "break", NULL }, handle_asyncagi_break, NULL, NULL, 1 },
02482    { { "channel", "status", NULL }, handle_channelstatus, NULL, NULL, 0 },
02483    { { "database", "del", NULL }, handle_dbdel, NULL, NULL, 1 },
02484    { { "database", "deltree", NULL }, handle_dbdeltree, NULL, NULL, 1 },
02485    { { "database", "get", NULL }, handle_dbget, NULL, NULL, 1 },
02486    { { "database", "put", NULL }, handle_dbput, NULL, NULL, 1 },
02487    { { "exec", NULL }, handle_exec, NULL, NULL, 1 },
02488    { { "get", "data", NULL }, handle_getdata, NULL, NULL, 0 },
02489    { { "get", "full", "variable", NULL }, handle_getvariablefull, NULL, NULL, 1 },
02490    { { "get", "option", NULL }, handle_getoption, NULL, NULL, 0 },
02491    { { "get", "variable", NULL }, handle_getvariable, NULL, NULL, 1 },
02492    { { "hangup", NULL }, handle_hangup, NULL, NULL, 0 },
02493    { { "noop", NULL }, handle_noop, NULL, NULL, 1 },
02494    { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar , 0 },
02495    { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext , 0 },
02496    { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile , 0 },
02497    { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha , 0 },
02498    { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits , 0 },
02499    { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber , 0 },
02500    { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic , 0 },
02501    { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate , 0 },
02502    { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime , 0 },
02503    { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime , 0 },
02504    { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage , 0 },
02505    { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext , 0 },
02506    { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup , 0 },
02507    { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid , 0 },
02508    { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext , 0 },
02509    { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension , 0 },
02510    { { "set", "music", NULL }, handle_setmusic, NULL, NULL, 0 },
02511    { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority , 0 },
02512    { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable , 1 },
02513    { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile , 0 },
02514    { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile , 0 },
02515    { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
02516    { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
02517    { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
02518    { { "speech", "create", NULL }, handle_speechcreate, "Creates a speech object", usage_speechcreate, 0 },
02519    { { "speech", "set", NULL }, handle_speechset, "Sets a speech engine setting", usage_speechset, 0 },
02520    { { "speech", "destroy", NULL }, handle_speechdestroy, "Destroys a speech object", usage_speechdestroy, 1 },
02521    { { "speech", "load", "grammar", NULL }, handle_speechloadgrammar, "Loads a grammar", usage_speechloadgrammar, 0 },
02522    { { "speech", "unload", "grammar", NULL }, handle_speechunloadgrammar, "Unloads a grammar", usage_speechunloadgrammar, 1 },
02523    { { "speech", "activate", "grammar", NULL }, handle_speechactivategrammar, "Activates a grammar", usage_speechactivategrammar, 0 },
02524    { { "speech", "deactivate", "grammar", NULL }, handle_speechdeactivategrammar, "Deactivates a grammar", usage_speechdeactivategrammar, 0 },
02525    { { "speech", "recognize", NULL }, handle_speechrecognize, "Recognizes speech", usage_speechrecognize, 0 },
02526 };
02527 
02528 static AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
02529 
02530 static char *help_workhorse(int fd, char *match[])
02531 {
02532    char fullcmd[MAX_CMD_LEN], matchstr[MAX_CMD_LEN];
02533    struct agi_command *e;
02534 
02535    if (match)
02536       ast_join(matchstr, sizeof(matchstr), match);
02537 
02538    ast_cli(fd, "%5.5s %30.30s   %s\n","Dead","Command","Description");
02539    AST_RWLIST_RDLOCK(&agi_commands);
02540    AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02541       if (!e->cmda[0])
02542          break;
02543       /* Hide commands that start with '_' */
02544       if ((e->cmda[0])[0] == '_')
02545          continue;
02546       ast_join(fullcmd, sizeof(fullcmd), e->cmda);
02547       if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
02548          continue;
02549       ast_cli(fd, "%5.5s %30.30s   %s\n", e->dead ? "Yes" : "No" , fullcmd, S_OR(e->summary, "Not available"));
02550    }
02551    AST_RWLIST_UNLOCK(&agi_commands);
02552 
02553    return CLI_SUCCESS;
02554 }
02555 
02556 int ast_agi_register(struct ast_module *mod, agi_command *cmd)
02557 {
02558    char fullcmd[MAX_CMD_LEN];
02559 
02560    ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02561 
02562    if (!find_command(cmd->cmda,1)) {
02563       cmd->docsrc = AST_STATIC_DOC;
02564       if (ast_strlen_zero(cmd->summary) && ast_strlen_zero(cmd->usage)) {
02565 #ifdef AST_XML_DOCS
02566          *((char **) &cmd->summary) = ast_xmldoc_build_synopsis("agi", fullcmd);
02567          *((char **) &cmd->usage) = ast_xmldoc_build_description("agi", fullcmd);
02568          *((char **) &cmd->syntax) = ast_xmldoc_build_syntax("agi", fullcmd);
02569          *((char **) &cmd->seealso) = ast_xmldoc_build_seealso("agi", fullcmd);
02570          *((enum ast_doc_src *) &cmd->docsrc) = AST_XML_DOC;
02571 #elif (!defined(HAVE_NULLSAFE_PRINTF))
02572          *((char **) &cmd->summary) = ast_strdup("");
02573          *((char **) &cmd->usage) = ast_strdup("");
02574          *((char **) &cmd->syntax) = ast_strdup("");
02575          *((char **) &cmd->seealso) = ast_strdup("");
02576 #endif
02577       }
02578 
02579       cmd->mod = mod;
02580       AST_RWLIST_WRLOCK(&agi_commands);
02581       AST_LIST_INSERT_TAIL(&agi_commands, cmd, list);
02582       AST_RWLIST_UNLOCK(&agi_commands);
02583       if (mod != ast_module_info->self)
02584          ast_module_ref(ast_module_info->self);
02585       ast_verb(2, "AGI Command '%s' registered\n",fullcmd);
02586       return 1;
02587    } else {
02588       ast_log(LOG_WARNING, "Command already registered!\n");
02589       return 0;
02590    }
02591 }
02592 
02593 int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
02594 {
02595    struct agi_command *e;
02596    int unregistered = 0;
02597    char fullcmd[MAX_CMD_LEN];
02598 
02599    ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02600 
02601    AST_RWLIST_WRLOCK(&agi_commands);
02602    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
02603       if (cmd == e) {
02604          AST_RWLIST_REMOVE_CURRENT(list);
02605          if (mod != ast_module_info->self)
02606             ast_module_unref(ast_module_info->self);
02607 #ifdef AST_XML_DOCS
02608          if (e->docsrc == AST_XML_DOC) {
02609             ast_free(e->summary);
02610             ast_free(e->usage);
02611             ast_free(e->syntax);
02612             ast_free(e->seealso);
02613             e->summary = NULL, e->usage = NULL;
02614             e->syntax = NULL, e->seealso = NULL;
02615          }
02616 #endif
02617          unregistered=1;
02618          break;
02619       }
02620    }
02621    AST_RWLIST_TRAVERSE_SAFE_END;
02622    AST_RWLIST_UNLOCK(&agi_commands);
02623    if (unregistered)
02624       ast_verb(2, "AGI Command '%s' unregistered\n",fullcmd);
02625    else
02626       ast_log(LOG_WARNING, "Unable to unregister command: '%s'!\n",fullcmd);
02627    return unregistered;
02628 }
02629 
02630 int ast_agi_register_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02631 {
02632    unsigned int i, x = 0;
02633 
02634    for (i = 0; i < len; i++) {
02635       if (ast_agi_register(mod, cmd + i) == 1) {
02636          x++;
02637          continue;
02638       }
02639 
02640       /* registration failed, unregister everything
02641          that had been registered up to that point
02642       */
02643       for (; x > 0; x--) {
02644          /* we are intentionally ignoring the
02645             result of ast_agi_unregister() here,
02646             but it should be safe to do so since
02647             we just registered these commands and
02648             the only possible way for unregistration
02649             to fail is if the command is not
02650             registered
02651          */
02652          (void) ast_agi_unregister(mod, cmd + x - 1);
02653       }
02654       return -1;
02655    }
02656 
02657    return 0;
02658 }
02659 
02660 int ast_agi_unregister_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02661 {
02662    unsigned int i;
02663    int res = 0;
02664 
02665    for (i = 0; i < len; i++) {
02666       /* remember whether any of the unregistration
02667          attempts failed... there is no recourse if
02668          any of them do
02669       */
02670       res |= ast_agi_unregister(mod, cmd + i);
02671    }
02672 
02673    return res;
02674 }
02675 
02676 static agi_command *find_command(char *cmds[], int exact)
02677 {
02678    int y, match;
02679    struct agi_command *e;
02680 
02681    AST_RWLIST_RDLOCK(&agi_commands);
02682    AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02683       if (!e->cmda[0])
02684          break;
02685       /* start optimistic */
02686       match = 1;
02687       for (y = 0; match && cmds[y]; y++) {
02688          /* If there are no more words in the command (and we're looking for
02689             an exact match) or there is a difference between the two words,
02690             then this is not a match */
02691          if (!e->cmda[y] && !exact)
02692             break;
02693          /* don't segfault if the next part of a command doesn't exist */
02694          if (!e->cmda[y]) {
02695             AST_RWLIST_UNLOCK(&agi_commands);
02696             return NULL;
02697          }
02698          if (strcasecmp(e->cmda[y], cmds[y]))
02699             match = 0;
02700       }
02701       /* If more words are needed to complete the command then this is not
02702          a candidate (unless we're looking for a really inexact answer  */
02703       if ((exact > -1) && e->cmda[y])
02704          match = 0;
02705       if (match) {
02706          AST_RWLIST_UNLOCK(&agi_commands);
02707          return e;
02708       }
02709    }
02710    AST_RWLIST_UNLOCK(&agi_commands);
02711    return NULL;
02712 }
02713 
02714 static int parse_args(char *s, int *max, char *argv[])
02715 {
02716    int x = 0, quoted = 0, escaped = 0, whitespace = 1;
02717    char *cur;
02718 
02719    cur = s;
02720    while(*s) {
02721       switch(*s) {
02722       case '"':
02723          /* If it's escaped, put a literal quote */
02724          if (escaped)
02725             goto normal;
02726          else
02727             quoted = !quoted;
02728          if (quoted && whitespace) {
02729             /* If we're starting a quote, coming off white space start a new word, too */
02730             argv[x++] = cur;
02731             whitespace=0;
02732          }
02733          escaped = 0;
02734       break;
02735       case ' ':
02736       case '\t':
02737          if (!quoted && !escaped) {
02738             /* If we're not quoted, mark this as whitespace, and
02739                end the previous argument */
02740             whitespace = 1;
02741             *(cur++) = '\0';
02742          } else
02743             /* Otherwise, just treat it as anything else */
02744             goto normal;
02745          break;
02746       case '\\':
02747          /* If we're escaped, print a literal, otherwise enable escaping */
02748          if (escaped) {
02749             goto normal;
02750          } else {
02751             escaped=1;
02752          }
02753          break;
02754       default:
02755 normal:
02756          if (whitespace) {
02757             if (x >= MAX_ARGS -1) {
02758                ast_log(LOG_WARNING, "Too many arguments, truncating\n");
02759                break;
02760             }
02761             /* Coming off of whitespace, start the next argument */
02762             argv[x++] = cur;
02763             whitespace=0;
02764          }
02765          *(cur++) = *s;
02766          escaped=0;
02767       }
02768       s++;
02769    }
02770    /* Null terminate */
02771    *(cur++) = '\0';
02772    argv[x] = NULL;
02773    *max = x;
02774    return 0;
02775 }
02776 
02777 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
02778 {
02779    char *argv[MAX_ARGS];
02780    int argc = MAX_ARGS, res;
02781    agi_command *c;
02782    const char *ami_res = "Unknown Result";
02783    char *ami_cmd = ast_strdupa(buf);
02784    int command_id = ast_random(), resultcode = 200;
02785 
02786    manager_event(EVENT_FLAG_AGI, "AGIExec",
02787          "SubEvent: Start\r\n"
02788          "Channel: %s\r\n"
02789          "CommandId: %d\r\n"
02790          "Command: %s\r\n", chan->name, command_id, ami_cmd);
02791    parse_args(buf, &argc, argv);
02792    if ((c = find_command(argv, 0)) && (!dead || (dead && c->dead))) {
02793       /* if this command wasnt registered by res_agi, be sure to usecount
02794       the module we are using */
02795       if (c->mod != ast_module_info->self)
02796          ast_module_ref(c->mod);
02797       /* If the AGI command being executed is an actual application (using agi exec)
02798       the app field will be updated in pbx_exec via handle_exec */
02799       if (chan->cdr && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
02800          ast_cdr_setapp(chan->cdr, "AGI", buf);
02801 
02802       res = c->handler(chan, agi, argc, argv);
02803       if (c->mod != ast_module_info->self)
02804          ast_module_unref(c->mod);
02805       switch (res) {
02806       case RESULT_SHOWUSAGE: ami_res = "Usage"; resultcode = 520; break;
02807       case RESULT_FAILURE: ami_res = "Failure"; resultcode = -1; break;
02808       case RESULT_SUCCESS: ami_res = "Success"; resultcode = 200; break;
02809       }
02810       manager_event(EVENT_FLAG_AGI, "AGIExec",
02811             "SubEvent: End\r\n"
02812             "Channel: %s\r\n"
02813             "CommandId: %d\r\n"
02814             "Command: %s\r\n"
02815             "ResultCode: %d\r\n"
02816             "Result: %s\r\n", chan->name, command_id, ami_cmd, resultcode, ami_res);
02817       switch(res) {
02818       case RESULT_SHOWUSAGE:
02819          if (ast_strlen_zero(c->usage)) {
02820             ast_agi_send(agi->fd, chan, "520 Invalid command syntax.  Proper usage not available.\n");
02821          } else {
02822             ast_agi_send(agi->fd, chan, "520-Invalid command syntax.  Proper usage follows:\n");
02823             ast_agi_send(agi->fd, chan, "%s", c->usage);
02824             ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
02825          }
02826          break;
02827       case RESULT_FAILURE:
02828          /* They've already given the failure.  We've been hung up on so handle this
02829             appropriately */
02830          return -1;
02831       }
02832    } else if ((c = find_command(argv, 0))) {
02833       ast_agi_send(agi->fd, chan, "511 Command Not Permitted on a dead channel\n");
02834       manager_event(EVENT_FLAG_AGI, "AGIExec",
02835             "SubEvent: End\r\n"
02836             "Channel: %s\r\n"
02837             "CommandId: %d\r\n"
02838             "Command: %s\r\n"
02839             "ResultCode: 511\r\n"
02840             "Result: Command not permitted on a dead channel\r\n", chan->name, command_id, ami_cmd);
02841    } else {
02842       ast_agi_send(agi->fd, chan, "510 Invalid or unknown command\n");
02843       manager_event(EVENT_FLAG_AGI, "AGIExec",
02844             "SubEvent: End\r\n"
02845             "Channel: %s\r\n"
02846             "CommandId: %d\r\n"
02847             "Command: %s\r\n"
02848             "ResultCode: 510\r\n"
02849             "Result: Invalid or unknown command\r\n", chan->name, command_id, ami_cmd);
02850    }
02851    return 0;
02852 }
02853 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
02854 {
02855    struct ast_channel *c;
02856    int outfd, ms, needhup = 0;
02857    enum agi_result returnstatus = AGI_RESULT_SUCCESS;
02858    struct ast_frame *f;
02859    char buf[AGI_BUF_LEN];
02860    char *res = NULL;
02861    FILE *readf;
02862    /* how many times we'll retry if ast_waitfor_nandfs will return without either
02863      channel or file descriptor in case select is interrupted by a system call (EINTR) */
02864    int retry = AGI_NANDFS_RETRY;
02865    int send_sighup;
02866    const char *sighup_str;
02867    
02868    ast_channel_lock(chan);
02869    sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
02870    send_sighup = ast_strlen_zero(sighup_str) || !ast_false(sighup_str);
02871    ast_channel_unlock(chan);
02872 
02873    if (!(readf = fdopen(agi->ctrl, "r"))) {
02874       ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
02875       if (send_sighup && pid > -1)
02876          kill(pid, SIGHUP);
02877       close(agi->ctrl);
02878       return AGI_RESULT_FAILURE;
02879    }
02880    
02881    setlinebuf(readf);
02882    setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
02883    for (;;) {
02884       if (needhup) {
02885          needhup = 0;
02886          dead = 1;
02887          if (send_sighup) {
02888             if (pid > -1) {
02889                kill(pid, SIGHUP);
02890             } else if (agi->fast) {
02891                send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02892             }
02893          }
02894       }
02895       ms = -1;
02896       c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
02897       if (c) {
02898          retry = AGI_NANDFS_RETRY;
02899          /* Idle the channel until we get a command */
02900          f = ast_read(c);
02901          if (!f) {
02902             ast_debug(1, "%s hungup\n", chan->name);
02903             returnstatus = AGI_RESULT_HANGUP;
02904             needhup = 1;
02905             continue;
02906          } else {
02907             /* If it's voice, write it to the audio pipe */
02908             if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
02909                /* Write, ignoring errors */
02910                if (write(agi->audio, f->data.ptr, f->datalen) < 0) {
02911                }
02912             }
02913             ast_frfree(f);
02914          }
02915       } else if (outfd > -1) {
02916          size_t len = sizeof(buf);
02917          size_t buflen = 0;
02918 
02919          retry = AGI_NANDFS_RETRY;
02920          buf[0] = '\0';
02921 
02922          while (buflen < (len - 1)) {
02923             res = fgets(buf + buflen, len, readf);
02924             if (feof(readf))
02925                break;
02926             if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))
02927                break;
02928             if (res != NULL && !agi->fast)
02929                break;
02930             buflen = strlen(buf);
02931             if (buflen && buf[buflen - 1] == '\n')
02932                break;
02933             len -= buflen;
02934             if (agidebug)
02935                ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
02936          }
02937 
02938          if (!buf[0]) {
02939             /* Program terminated */
02940             if (returnstatus) {
02941                returnstatus = -1;
02942             }
02943             ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", chan->name, request, returnstatus);
02944             if (pid > 0)
02945                waitpid(pid, status, 0);
02946             /* No need to kill the pid anymore, since they closed us */
02947             pid = -1;
02948             break;
02949          }
02950 
02951          /* Special case for inability to execute child process */
02952          if (*buf && strncasecmp(buf, "failure", 7) == 0) {
02953             returnstatus = AGI_RESULT_FAILURE;
02954             break;
02955          }
02956 
02957          /* get rid of trailing newline, if any */
02958          if (*buf && buf[strlen(buf) - 1] == '\n')
02959             buf[strlen(buf) - 1] = 0;
02960          if (agidebug)
02961             ast_verbose("<%s>AGI Rx << %s\n", chan->name, buf);
02962          returnstatus |= agi_handle_command(chan, agi, buf, dead);
02963          /* If the handle_command returns -1, we need to stop */
02964          if (returnstatus < 0) {
02965             needhup = 1;
02966             continue;
02967          }
02968       } else {
02969          if (--retry <= 0) {
02970             ast_log(LOG_WARNING, "No channel, no fd?\n");
02971             returnstatus = AGI_RESULT_FAILURE;
02972             break;
02973          }
02974       }
02975    }
02976    if (agi->speech) {
02977       ast_speech_destroy(agi->speech);
02978    }
02979    /* Notify process */
02980    if (send_sighup) {
02981       if (pid > -1) {
02982          if (kill(pid, SIGHUP)) {
02983             ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
02984          } else { /* Give the process a chance to die */
02985             usleep(1);
02986          }
02987          waitpid(pid, status, WNOHANG);
02988       } else if (agi->fast) {
02989          send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02990       }
02991    }
02992    fclose(readf);
02993    return returnstatus;
02994 }
02995 
02996 static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02997 {
02998    struct agi_command *command;
02999    char fullcmd[MAX_CMD_LEN];
03000    int error = 0;
03001 
03002    switch (cmd) {
03003    case CLI_INIT:
03004       e->command = "agi show commands [topic]";
03005       e->usage =
03006          "Usage: agi show commands [topic] <topic>\n"
03007          "       When called with a topic as an argument, displays usage\n"
03008          "       information on the given command.  If called without a\n"
03009          "       topic, it provides a list of AGI commands.\n";
03010    case CLI_GENERATE:
03011       return NULL;
03012    }
03013    if (a->argc < e->args - 1 || (a->argc >= e->args && strcasecmp(a->argv[e->args - 1], "topic")))
03014       return CLI_SHOWUSAGE;
03015    if (a->argc > e->args - 1) {
03016       command = find_command(a->argv + e->args, 1);
03017       if (command) {
03018          char *synopsis = NULL, *description = NULL, *syntax = NULL, *seealso = NULL;
03019          char info[30 + MAX_CMD_LEN];              /* '-= Info about...' */
03020          char infotitle[30 + MAX_CMD_LEN + AST_TERM_MAX_ESCAPE_CHARS];  /* '-= Info about...' with colors */
03021          char syntitle[11 + AST_TERM_MAX_ESCAPE_CHARS];        /* [Syntax]\n with colors */
03022          char desctitle[15 + AST_TERM_MAX_ESCAPE_CHARS];       /* [Description]\n with colors */
03023          char deadtitle[13 + AST_TERM_MAX_ESCAPE_CHARS];       /* [Runs Dead]\n with colors */
03024          char deadcontent[3 + AST_TERM_MAX_ESCAPE_CHARS];      /* 'Yes' or 'No' with colors */
03025          char seealsotitle[12 + AST_TERM_MAX_ESCAPE_CHARS];    /* [See Also]\n with colors */
03026          char stxtitle[10 + AST_TERM_MAX_ESCAPE_CHARS];        /* [Syntax]\n with colors */
03027          size_t synlen, desclen, seealsolen, stxlen;
03028 
03029          term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, sizeof(syntitle));
03030          term_color(desctitle, "[Description]\n", COLOR_MAGENTA, 0, sizeof(desctitle));
03031          term_color(deadtitle, "[Runs Dead]\n", COLOR_MAGENTA, 0, sizeof(deadtitle));
03032          term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, sizeof(seealsotitle));
03033          term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, sizeof(stxtitle));
03034          term_color(deadcontent, command->dead ? "Yes" : "No", COLOR_CYAN, 0, sizeof(deadcontent));
03035 
03036          ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
03037          snprintf(info, sizeof(info), "\n  -= Info about agi '%s' =- ", fullcmd);
03038          term_color(infotitle, info, COLOR_CYAN, 0, sizeof(infotitle));
03039 #ifdef AST_XML_DOCS
03040          if (command->docsrc == AST_XML_DOC) {
03041             synopsis = ast_xmldoc_printable(S_OR(command->summary, "Not available"), 1);
03042             description = ast_xmldoc_printable(S_OR(command->usage, "Not available"), 1);
03043             seealso = ast_xmldoc_printable(S_OR(command->seealso, "Not available"), 1);
03044             if (!seealso || !description || !synopsis) {
03045                error = 1;
03046                goto return_cleanup;
03047             }
03048          } else
03049 #endif
03050          {
03051             synlen = strlen(S_OR(command->summary, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
03052             synopsis = ast_malloc(synlen);
03053 
03054             desclen = strlen(S_OR(command->usage, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
03055             description = ast_malloc(desclen);
03056 
03057             seealsolen = strlen(S_OR(command->seealso, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
03058             seealso = ast_malloc(seealsolen);
03059 
03060             if (!synopsis || !description || !seealso) {
03061                error = 1;
03062                goto return_cleanup;
03063             }
03064             term_color(synopsis, S_OR(command->summary, "Not available"), COLOR_CYAN, 0, synlen);
03065             term_color(description, S_OR(command->usage, "Not available"), COLOR_CYAN, 0, desclen);
03066             term_color(seealso, S_OR(command->seealso, "Not available"), COLOR_CYAN, 0, seealsolen);
03067          }
03068 
03069          stxlen = strlen(S_OR(command->syntax, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
03070          syntax = ast_malloc(stxlen);
03071          if (!syntax) {
03072             error = 1;
03073             goto return_cleanup;
03074          }
03075          term_color(syntax, S_OR(command->syntax, "Not available"), COLOR_CYAN, 0, stxlen);
03076 
03077          ast_cli(a->fd, "%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n", infotitle, stxtitle, syntax,
03078                desctitle, description, syntitle, synopsis, deadtitle, deadcontent,
03079                seealsotitle, seealso);
03080 return_cleanup:
03081          ast_free(synopsis);
03082          ast_free(description);
03083          ast_free(syntax);
03084          ast_free(seealso);
03085       } else {
03086          if (find_command(a->argv + e->args, -1)) {
03087             return help_workhorse(a->fd, a->argv + e->args);
03088          } else {
03089             ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
03090             ast_cli(a->fd, "No such command '%s'.\n", fullcmd);
03091          }
03092       }
03093    } else {
03094       return help_workhorse(a->fd, NULL);
03095    }
03096    return (error ? CLI_FAILURE : CLI_SUCCESS);
03097 }
03098 
03099 /*! \brief Convert string to use HTML escaped characters
03100    \note Maybe this should be a generic function?
03101 */
03102 static void write_html_escaped(FILE *htmlfile, char *str)
03103 {
03104    char *cur = str;
03105 
03106    while(*cur) {
03107       switch (*cur) {
03108       case '<':
03109          fprintf(htmlfile, "%s", "&lt;");
03110          break;
03111       case '>':
03112          fprintf(htmlfile, "%s", "&gt;");
03113          break;
03114       case '&':
03115          fprintf(htmlfile, "%s", "&amp;");
03116          break;
03117       case '"':
03118          fprintf(htmlfile, "%s", "&quot;");
03119          break;
03120       default:
03121          fprintf(htmlfile, "%c", *cur);
03122          break;
03123       }
03124       cur++;
03125    }
03126 
03127    return;
03128 }
03129 
03130 static int write_htmldump(char *filename)
03131 {
03132    struct agi_command *command;
03133    char fullcmd[MAX_CMD_LEN];
03134    FILE *htmlfile;
03135 
03136    if (!(htmlfile = fopen(filename, "wt")))
03137       return -1;
03138 
03139    fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
03140    fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
03141    fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
03142 
03143    AST_RWLIST_RDLOCK(&agi_commands);
03144    AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
03145 #ifdef AST_XML_DOCS
03146       char *stringptmp;
03147 #endif
03148       char *tempstr, *stringp;
03149 
03150       if (!command->cmda[0])  /* end ? */
03151          break;
03152       /* Hide commands that start with '_' */
03153       if ((command->cmda[0])[0] == '_')
03154          continue;
03155       ast_join(fullcmd, sizeof(fullcmd), command->cmda);
03156 
03157       fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
03158       fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
03159 #ifdef AST_XML_DOCS
03160       stringptmp = ast_xmldoc_printable(command->usage, 0);
03161       stringp = stringptmp;
03162 #else
03163       stringp = command->usage;
03164 #endif
03165       tempstr = strsep(&stringp, "\n");
03166 
03167       fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">");
03168       write_html_escaped(htmlfile, tempstr);
03169       fprintf(htmlfile, "</TD></TR>\n");
03170       fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
03171 
03172       while ((tempstr = strsep(&stringp, "\n")) != NULL) {
03173          write_html_escaped(htmlfile, tempstr);
03174          fprintf(htmlfile, "<BR>\n");
03175       }
03176       fprintf(htmlfile, "</TD></TR>\n");
03177       fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
03178 #ifdef AST_XML_DOCS
03179       ast_free(stringptmp);
03180 #endif
03181    }
03182    AST_RWLIST_UNLOCK(&agi_commands);
03183    fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
03184    fclose(htmlfile);
03185    return 0;
03186 }
03187 
03188 static char *handle_cli_agi_dump_html(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03189 {
03190    switch (cmd) {
03191    case CLI_INIT:
03192       e->command = "agi dump html";
03193       e->usage =
03194          "Usage: agi dump html <filename>\n"
03195          "       Dumps the AGI command list in HTML format to the given\n"
03196          "       file.\n";
03197       return NULL;
03198    case CLI_GENERATE:
03199       return NULL;
03200    }
03201    if (a->argc != e->args + 1)
03202       return CLI_SHOWUSAGE;
03203 
03204    if (write_htmldump(a->argv[e->args]) < 0) {
03205       ast_cli(a->fd, "Could not create file '%s'\n", a->argv[e->args]);
03206       return CLI_SHOWUSAGE;
03207    }
03208    ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[e->args]);
03209    return CLI_SUCCESS;
03210 }
03211 
03212 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
03213 {
03214    enum agi_result res;
03215    char buf[AGI_BUF_LEN] = "", *tmp = buf;
03216    int fds[2], efd = -1, pid;
03217    AST_DECLARE_APP_ARGS(args,
03218       AST_APP_ARG(arg)[MAX_ARGS];
03219    );
03220    AGI agi;
03221 
03222    if (ast_strlen_zero(data)) {
03223       ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
03224       return -1;
03225    }
03226    if (dead)
03227       ast_debug(3, "Hungup channel detected, running agi in dead mode.\n");
03228    ast_copy_string(buf, data, sizeof(buf));
03229    memset(&agi, 0, sizeof(agi));
03230    AST_STANDARD_APP_ARGS(args, tmp);
03231    args.argv[args.argc] = NULL;
03232 #if 0
03233     /* Answer if need be */
03234    if (chan->_state != AST_STATE_UP) {
03235       if (ast_answer(chan))
03236          return -1;
03237    }
03238 #endif
03239    res = launch_script(chan, args.argv[0], args.argv, fds, enhanced ? &efd : NULL, &pid);
03240    /* Async AGI do not require run_agi(), so just proceed if normal AGI
03241       or Fast AGI are setup with success. */
03242    if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
03243       int status = 0;
03244       agi.fd = fds[1];
03245       agi.ctrl = fds[0];
03246       agi.audio = efd;
03247       agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
03248       res = run_agi(chan, args.argv[0], &agi, pid, &status, dead, args.argc, args.argv);
03249       /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
03250       if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
03251          res = AGI_RESULT_FAILURE;
03252       if (fds[1] != fds[0])
03253          close(fds[1]);
03254       if (efd > -1)
03255          close(efd);
03256    }
03257    ast_safe_fork_cleanup();
03258 
03259    switch (res) {
03260    case AGI_RESULT_SUCCESS:
03261    case AGI_RESULT_SUCCESS_FAST:
03262    case AGI_RESULT_SUCCESS_ASYNC:
03263       pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
03264       break;
03265    case AGI_RESULT_FAILURE:
03266       pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
03267       break;
03268    case AGI_RESULT_NOTFOUND:
03269       pbx_builtin_setvar_helper(chan, "AGISTATUS", "NOTFOUND");
03270       break;
03271    case AGI_RESULT_HANGUP:
03272       pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
03273       return -1;
03274    }
03275 
03276    return 0;
03277 }
03278 
03279 static int agi_exec(struct ast_channel *chan, void *data)
03280 {
03281    if (!ast_check_hangup(chan))
03282       return agi_exec_full(chan, data, 0, 0);
03283    else
03284       return agi_exec_full(chan, data, 0, 1);
03285 }
03286 
03287 static int eagi_exec(struct ast_channel *chan, void *data)
03288 {
03289    int readformat, res;
03290 
03291    if (ast_check_hangup(chan)) {
03292       ast_log(LOG_ERROR, "EAGI cannot be run on a dead/hungup channel, please use AGI.\n");
03293       return 0;
03294    }
03295    readformat = chan->readformat;
03296    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
03297       ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
03298       return -1;
03299    }
03300    res = agi_exec_full(chan, data, 1, 0);
03301    if (!res) {
03302       if (ast_set_read_format(chan, readformat)) {
03303          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
03304       }
03305    }
03306    return res;
03307 }
03308 
03309 static int deadagi_exec(struct ast_channel *chan, void *data)
03310 {
03311    ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!\n");
03312    return agi_exec(chan, data);
03313 }
03314 
03315 static struct ast_cli_entry cli_agi[] = {
03316    AST_CLI_DEFINE(handle_cli_agi_add_cmd,   "Add AGI command to a channel in Async AGI"),
03317    AST_CLI_DEFINE(handle_cli_agi_debug,     "Enable/Disable AGI debugging"),
03318    AST_CLI_DEFINE(handle_cli_agi_show,      "List AGI commands or specific help"),
03319    AST_CLI_DEFINE(handle_cli_agi_dump_html, "Dumps a list of AGI commands in HTML format")
03320 };
03321 
03322 static int unload_module(void)
03323 {
03324    ast_cli_unregister_multiple(cli_agi, ARRAY_LEN(cli_agi));
03325    /* we can safely ignore the result of ast_agi_unregister_multiple() here, since it cannot fail, as
03326       we know that these commands were registered by this module and are still registered
03327    */
03328    (void) ast_agi_unregister_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03329    ast_unregister_application(eapp);
03330    ast_unregister_application(deadapp);
03331    ast_manager_unregister("AGI");
03332    return ast_unregister_application(app);
03333 }
03334 
03335 static int load_module(void)
03336 {
03337    ast_cli_register_multiple(cli_agi, ARRAY_LEN(cli_agi));
03338    /* we can safely ignore the result of ast_agi_register_multiple() here, since it cannot fail, as
03339       no other commands have been registered yet
03340    */
03341    (void) ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03342    ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
03343    ast_register_application(eapp, eagi_exec, esynopsis, descrip);
03344    ast_manager_register2("AGI", EVENT_FLAG_AGI, action_add_agi_cmd, "Add an AGI command to execute by Async AGI", mandescr_asyncagi);
03345    return ast_register_application(app, agi_exec, synopsis, descrip);
03346 }
03347 
03348 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
03349       .load = load_module,
03350       .unload = unload_module,
03351       );