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

Generated on Wed Aug 18 22:33:55 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7