Fri Jun 19 12:09:50 2009

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

Generated on Fri Jun 19 12:09:50 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7