Tue Mar 2 17:31:51 2010

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

Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1