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

Generated on Thu Jul 9 13:40:39 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7