Fri Aug 24 02:22:16 2007

Asterisk developer's documentation


res_agi.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief AGI - the Asterisk Gateway Interface
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00029 
00030 #include <sys/types.h>
00031 #include <netdb.h>
00032 #include <sys/socket.h>
00033 #include <netinet/in.h>
00034 #include <netinet/tcp.h>
00035 #include <arpa/inet.h>
00036 #include <math.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <string.h>
00040 #include <stdlib.h>
00041 #include <signal.h>
00042 #include <sys/time.h>
00043 #include <stdio.h>
00044 #include <fcntl.h>
00045 #include <errno.h>
00046 #include <sys/stat.h>
00047 #include <sys/wait.h>
00048 
00049 #include "asterisk/file.h"
00050 #include "asterisk/logger.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/astdb.h"
00055 #include "asterisk/callerid.h"
00056 #include "asterisk/cli.h"
00057 #include "asterisk/logger.h"
00058 #include "asterisk/options.h"
00059 #include "asterisk/image.h"
00060 #include "asterisk/say.h"
00061 #include "asterisk/app.h"
00062 #include "asterisk/dsp.h"
00063 #include "asterisk/musiconhold.h"
00064 #include "asterisk/utils.h"
00065 #include "asterisk/lock.h"
00066 #include "asterisk/strings.h"
00067 #include "asterisk/agi.h"
00068 
00069 #define MAX_ARGS 128
00070 #define MAX_COMMANDS 128
00071 
00072 /* Recycle some stuff from the CLI interface */
00073 #define fdprintf agi_debug_cli
00074 
00075 static char *app = "AGI";
00076 
00077 static char *eapp = "EAGI";
00078 
00079 static char *deadapp = "DeadAGI";
00080 
00081 static char *synopsis = "Executes an AGI compliant application";
00082 static char *esynopsis = "Executes an EAGI compliant application";
00083 static char *deadsynopsis = "Executes AGI on a hungup channel";
00084 
00085 static char *descrip =
00086 "  [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
00087 "program on a channel. AGI allows Asterisk to launch external programs\n"
00088 "written in any language to control a telephony channel, play audio,\n"
00089 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
00090 "and stdout.\n"
00091 "  This channel will stop dialplan execution on hangup inside of this\n"
00092 "application, except when using DeadAGI.  Otherwise, dialplan execution\n"
00093 "will continue normally.\n"
00094 "  A locally executed AGI script will receive SIGHUP on hangup from the channel\n"
00095 "except when using DeadAGI. This can be disabled by setting the AGISIGHUP channel\n"
00096 "variable to \"no\" before executing the AGI application.\n"
00097 "  Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
00098 "on file descriptor 3\n\n"
00099 "  Use the CLI command 'agi show' to list available agi commands\n"
00100 "  This application sets the following channel variable upon completion:\n"
00101 "     AGISTATUS      The status of the attempt to the run the AGI script\n"
00102 "                    text string, one of SUCCESS | FAILED | HANGUP\n";
00103 
00104 static int agidebug = 0;
00105 
00106 #define TONE_BLOCK_SIZE 200
00107 
00108 /* Max time to connect to an AGI remote host */
00109 #define MAX_AGI_CONNECT 2000
00110 
00111 #define AGI_PORT 4573
00112 
00113 enum agi_result {
00114    AGI_RESULT_SUCCESS,
00115    AGI_RESULT_FAILURE,
00116    AGI_RESULT_HANGUP
00117 };
00118 
00119 static int agi_debug_cli(int fd, char *fmt, ...)
00120 {
00121    char *stuff;
00122    int res = 0;
00123 
00124    va_list ap;
00125    va_start(ap, fmt);
00126    res = vasprintf(&stuff, fmt, ap);
00127    va_end(ap);
00128    if (res == -1) {
00129       ast_log(LOG_ERROR, "Out of memory\n");
00130    } else {
00131       if (agidebug)
00132          ast_verbose("AGI Tx >> %s", stuff); /* \n provided by caller */
00133       res = ast_carefulwrite(fd, stuff, strlen(stuff), 100);
00134       free(stuff);
00135    }
00136 
00137    return res;
00138 }
00139 
00140 /* launch_netscript: The fastagi handler.
00141    FastAGI defaults to port 4573 */
00142 static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
00143 {
00144    int s;
00145    int flags;
00146    struct pollfd pfds[1];
00147    char *host;
00148    char *c; int port = AGI_PORT;
00149    char *script="";
00150    struct sockaddr_in sin;
00151    struct hostent *hp;
00152    struct ast_hostent ahp;
00153    int res;
00154 
00155    /* agiusl is "agi://host.domain[:port][/script/name]" */
00156    host = ast_strdupa(agiurl + 6);  /* Remove agi:// */
00157    /* Strip off any script name */
00158    if ((c = strchr(host, '/'))) {
00159       *c = '\0';
00160       c++;
00161       script = c;
00162    }
00163    if ((c = strchr(host, ':'))) {
00164       *c = '\0';
00165       c++;
00166       port = atoi(c);
00167    }
00168    if (efd) {
00169       ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
00170       return -1;
00171    }
00172    hp = ast_gethostbyname(host, &ahp);
00173    if (!hp) {
00174       ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
00175       return -1;
00176    }
00177    s = socket(AF_INET, SOCK_STREAM, 0);
00178    if (s < 0) {
00179       ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
00180       return -1;
00181    }
00182    flags = fcntl(s, F_GETFL);
00183    if (flags < 0) {
00184       ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
00185       close(s);
00186       return -1;
00187    }
00188    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
00189       ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
00190       close(s);
00191       return -1;
00192    }
00193    memset(&sin, 0, sizeof(sin));
00194    sin.sin_family = AF_INET;
00195    sin.sin_port = htons(port);
00196    memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
00197    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
00198       ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
00199       close(s);
00200       return AGI_RESULT_FAILURE;
00201    }
00202 
00203    pfds[0].fd = s;
00204    pfds[0].events = POLLOUT;
00205    while ((res = poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
00206       if (errno != EINTR) {
00207          if (!res) {
00208             ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
00209                agiurl, MAX_AGI_CONNECT);
00210          } else
00211             ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00212          close(s);
00213          return AGI_RESULT_FAILURE;
00214       }
00215    }
00216 
00217    if (fdprintf(s, "agi_network: yes\n") < 0) {
00218       if (errno != EINTR) {
00219          ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00220          close(s);
00221          return AGI_RESULT_FAILURE;
00222       }
00223    }
00224 
00225    /* If we have a script parameter, relay it to the fastagi server */
00226    /* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */
00227    if (!ast_strlen_zero(script))
00228       fdprintf(s, "agi_network_script: %s\n", script);
00229 
00230    if (option_debug > 3)
00231       ast_log(LOG_DEBUG, "Wow, connected!\n");
00232    fds[0] = s;
00233    fds[1] = s;
00234    *opid = -1;
00235    return AGI_RESULT_SUCCESS;
00236 }
00237 
00238 static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
00239 {
00240    char tmp[256];
00241    int pid;
00242    int toast[2];
00243    int fromast[2];
00244    int audio[2];
00245    int x;
00246    int res;
00247    sigset_t signal_set, old_set;
00248    struct stat statbuf;
00249 
00250    if (!strncasecmp(script, "agi://", 6))
00251       return launch_netscript(script, argv, fds, efd, opid);
00252    
00253    if (script[0] != '/') {
00254       snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
00255       script = tmp;
00256    }
00257         if (stat(script, &statbuf) < 0) {
00258                 ast_log(LOG_WARNING, "Unable to execute %s: File does not exist\n", script);
00259                 return -1;
00260         }
00261         if (!(statbuf.st_mode & S_IXUSR)) {
00262                 ast_log(LOG_WARNING, "Unable to execute %s: File is not executable\n", script);
00263                 return -1;
00264         }
00265    if (pipe(toast)) {
00266       ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
00267       return AGI_RESULT_FAILURE;
00268    }
00269    if (pipe(fromast)) {
00270       ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
00271       close(toast[0]);
00272       close(toast[1]);
00273       return AGI_RESULT_FAILURE;
00274    }
00275    if (efd) {
00276       if (pipe(audio)) {
00277          ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
00278          close(fromast[0]);
00279          close(fromast[1]);
00280          close(toast[0]);
00281          close(toast[1]);
00282          return AGI_RESULT_FAILURE;
00283       }
00284       res = fcntl(audio[1], F_GETFL);
00285       if (res > -1) 
00286          res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
00287       if (res < 0) {
00288          ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
00289          close(fromast[0]);
00290          close(fromast[1]);
00291          close(toast[0]);
00292          close(toast[1]);
00293          close(audio[0]);
00294          close(audio[1]);
00295          return AGI_RESULT_FAILURE;
00296       }
00297    }
00298 
00299    /* Block SIGHUP during the fork - prevents a race */
00300    sigfillset(&signal_set);
00301    pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
00302    pid = fork();
00303    if (pid < 0) {
00304       ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00305       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00306       return AGI_RESULT_FAILURE;
00307    }
00308    if (!pid) {
00309       /* Pass paths to AGI via environmental variables */
00310       setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1);
00311       setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1);
00312       setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1);
00313       setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1);
00314       setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1);
00315       setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1);
00316       setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1);
00317       setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1);
00318       setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1);
00319       setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1);
00320       setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1);
00321 
00322       /* Don't run AGI scripts with realtime priority -- it causes audio stutter */
00323       ast_set_priority(0);
00324 
00325       /* Redirect stdin and out, provide enhanced audio channel if desired */
00326       dup2(fromast[0], STDIN_FILENO);
00327       dup2(toast[1], STDOUT_FILENO);
00328       if (efd) {
00329          dup2(audio[0], STDERR_FILENO + 1);
00330       } else {
00331          close(STDERR_FILENO + 1);
00332       }
00333 
00334       /* Before we unblock our signals, return our trapped signals back to the defaults */
00335       signal(SIGHUP, SIG_DFL);
00336       signal(SIGCHLD, SIG_DFL);
00337       signal(SIGINT, SIG_DFL);
00338       signal(SIGURG, SIG_DFL);
00339       signal(SIGTERM, SIG_DFL);
00340       signal(SIGPIPE, SIG_DFL);
00341       signal(SIGXFSZ, SIG_DFL);
00342 
00343       /* unblock important signal handlers */
00344       if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
00345          ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno));
00346          _exit(1);
00347       }
00348 
00349       /* Close everything but stdin/out/error */
00350       for (x=STDERR_FILENO + 2;x<1024;x++) 
00351          close(x);
00352 
00353       /* Execute script */
00354       /* XXX argv should be deprecated in favor of passing agi_argX paramaters */
00355       execv(script, argv);
00356       /* Can't use ast_log since FD's are closed */
00357       fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno));
00358       fflush(stdout);
00359       _exit(1);
00360    }
00361    pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00362    if (option_verbose > 2) 
00363       ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
00364    fds[0] = toast[0];
00365    fds[1] = fromast[1];
00366    if (efd) {
00367       *efd = audio[1];
00368    }
00369    /* close what we're not using in the parent */
00370    close(toast[1]);
00371    close(fromast[0]);
00372 
00373    if (efd)
00374       close(audio[0]);
00375 
00376    *opid = pid;
00377    return AGI_RESULT_SUCCESS;
00378 }
00379 
00380 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[])
00381 {
00382    int count;
00383 
00384    /* Print initial environment, with agi_request always being the first
00385       thing */
00386    fdprintf(fd, "agi_request: %s\n", request);
00387    fdprintf(fd, "agi_channel: %s\n", chan->name);
00388    fdprintf(fd, "agi_language: %s\n", chan->language);
00389    fdprintf(fd, "agi_type: %s\n", chan->tech->type);
00390    fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
00391 
00392    /* ANI/DNIS */
00393    fdprintf(fd, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown"));
00394    fdprintf(fd, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown"));
00395    fdprintf(fd, "agi_callingpres: %d\n", chan->cid.cid_pres);
00396    fdprintf(fd, "agi_callingani2: %d\n", chan->cid.cid_ani2);
00397    fdprintf(fd, "agi_callington: %d\n", chan->cid.cid_ton);
00398    fdprintf(fd, "agi_callingtns: %d\n", chan->cid.cid_tns);
00399    fdprintf(fd, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown"));
00400    fdprintf(fd, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown"));
00401 
00402    /* Context information */
00403    fdprintf(fd, "agi_context: %s\n", chan->context);
00404    fdprintf(fd, "agi_extension: %s\n", chan->exten);
00405    fdprintf(fd, "agi_priority: %d\n", chan->priority);
00406    fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
00407 
00408    /* User information */
00409    fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
00410     
00411    /* Send any parameters to the fastagi server that have been passed via the agi application */
00412    /* Agi application paramaters take the form of: AGI(/path/to/example/script|${EXTEN}) */
00413    for(count = 1; count < argc; count++)
00414       fdprintf(fd, "agi_arg_%d: %s\n", count, argv[count]);
00415 
00416    /* End with empty return */
00417    fdprintf(fd, "\n");
00418 }
00419 
00420 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00421 {
00422    int res;
00423    res = 0;
00424    if (chan->_state != AST_STATE_UP) {
00425       /* Answer the chan */
00426       res = ast_answer(chan);
00427    }
00428    fdprintf(agi->fd, "200 result=%d\n", res);
00429    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00430 }
00431 
00432 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00433 {
00434    int res;
00435    int to;
00436    if (argc != 4)
00437       return RESULT_SHOWUSAGE;
00438    if (sscanf(argv[3], "%d", &to) != 1)
00439       return RESULT_SHOWUSAGE;
00440    res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
00441    fdprintf(agi->fd, "200 result=%d\n", res);
00442    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00443 }
00444 
00445 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00446 {
00447    int res;
00448    if (argc != 3)
00449       return RESULT_SHOWUSAGE;
00450    /* At the moment, the parser (perhaps broken) returns with
00451       the last argument PLUS the newline at the end of the input
00452       buffer. This probably needs to be fixed, but I wont do that
00453       because other stuff may break as a result. The right way
00454       would probably be to strip off the trailing newline before
00455       parsing, then here, add a newline at the end of the string
00456       before sending it to ast_sendtext --DUDE */
00457    res = ast_sendtext(chan, argv[2]);
00458    fdprintf(agi->fd, "200 result=%d\n", res);
00459    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00460 }
00461 
00462 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00463 {
00464    int res;
00465    if (argc != 3)
00466       return RESULT_SHOWUSAGE;
00467    res = ast_recvchar(chan,atoi(argv[2]));
00468    if (res == 0) {
00469       fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
00470       return RESULT_SUCCESS;
00471    }
00472    if (res > 0) {
00473       fdprintf(agi->fd, "200 result=%d\n", res);
00474       return RESULT_SUCCESS;
00475    }
00476    else {
00477       fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
00478       return RESULT_FAILURE;
00479    }
00480 }
00481 
00482 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00483 {
00484    char *buf;
00485    
00486    if (argc != 3)
00487       return RESULT_SHOWUSAGE;
00488    buf = ast_recvtext(chan,atoi(argv[2]));
00489    if (buf) {
00490       fdprintf(agi->fd, "200 result=1 (%s)\n", buf);
00491       free(buf);
00492    } else { 
00493       fdprintf(agi->fd, "200 result=-1\n");
00494    }
00495    return RESULT_SUCCESS;
00496 }
00497 
00498 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00499 {
00500    int res,x;
00501    if (argc != 3)
00502       return RESULT_SHOWUSAGE;
00503    if (!strncasecmp(argv[2],"on",2)) 
00504       x = 1; 
00505    else 
00506       x = 0;
00507    if (!strncasecmp(argv[2],"mate",4)) 
00508       x = 2;
00509    if (!strncasecmp(argv[2],"tdd",3))
00510       x = 1;
00511    res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
00512    if (res != RESULT_SUCCESS)
00513       fdprintf(agi->fd, "200 result=0\n");
00514    else
00515       fdprintf(agi->fd, "200 result=1\n");
00516    return RESULT_SUCCESS;
00517 }
00518 
00519 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00520 {
00521    int res;
00522    if (argc != 3)
00523       return RESULT_SHOWUSAGE;
00524    res = ast_send_image(chan, argv[2]);
00525    if (!ast_check_hangup(chan))
00526       res = 0;
00527    fdprintf(agi->fd, "200 result=%d\n", res);
00528    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00529 }
00530 
00531 static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00532 {
00533    int res = 0;
00534    int skipms = 3000;
00535    char *fwd = NULL;
00536    char *rev = NULL;
00537    char *pause = NULL;
00538    char *stop = NULL;
00539 
00540    if (argc < 5 || argc > 9)
00541       return RESULT_SHOWUSAGE;
00542 
00543    if (!ast_strlen_zero(argv[4]))
00544       stop = argv[4];
00545    else
00546       stop = NULL;
00547    
00548    if ((argc > 5) && (sscanf(argv[5], "%d", &skipms) != 1))
00549       return RESULT_SHOWUSAGE;
00550 
00551    if (argc > 6 && !ast_strlen_zero(argv[6]))
00552       fwd = argv[6];
00553    else
00554       fwd = "#";
00555 
00556    if (argc > 7 && !ast_strlen_zero(argv[7]))
00557       rev = argv[7];
00558    else
00559       rev = "*";
00560    
00561    if (argc > 8 && !ast_strlen_zero(argv[8]))
00562       pause = argv[8];
00563    else
00564       pause = NULL;
00565    
00566    res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, pause, NULL, skipms);
00567    
00568    fdprintf(agi->fd, "200 result=%d\n", res);
00569 
00570    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00571 }
00572 
00573 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00574 {
00575    int res;
00576    int vres;   
00577    struct ast_filestream *fs;
00578    struct ast_filestream *vfs;
00579    long sample_offset = 0;
00580    long max_length;
00581    char *edigits = "";
00582 
00583    if (argc < 4 || argc > 5)
00584       return RESULT_SHOWUSAGE;
00585 
00586    if (argv[3]) 
00587       edigits = argv[3];
00588 
00589    if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
00590       return RESULT_SHOWUSAGE;
00591    
00592    fs = ast_openstream(chan, argv[2], chan->language);   
00593    
00594    if (!fs) {
00595       fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
00596       return RESULT_SUCCESS;
00597    }  
00598    vfs = ast_openvstream(chan, argv[2], chan->language);
00599    if (vfs)
00600       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
00601       
00602    if (option_verbose > 2)
00603       ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
00604 
00605    ast_seekstream(fs, 0, SEEK_END);
00606    max_length = ast_tellstream(fs);
00607    ast_seekstream(fs, sample_offset, SEEK_SET);
00608    res = ast_applystream(chan, fs);
00609    if (vfs)
00610       vres = ast_applystream(chan, vfs);
00611    ast_playstream(fs);
00612    if (vfs)
00613       ast_playstream(vfs);
00614    
00615    res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
00616    /* this is to check for if ast_waitstream closed the stream, we probably are at
00617     * the end of the stream, return that amount, else check for the amount */
00618    sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
00619    ast_stopstream(chan);
00620    if (res == 1) {
00621       /* Stop this command, don't print a result line, as there is a new command */
00622       return RESULT_SUCCESS;
00623    }
00624    fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
00625    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00626 }
00627 
00628 /* get option - really similar to the handle_streamfile, but with a timeout */
00629 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00630 {
00631    int res;
00632    int vres;   
00633    struct ast_filestream *fs;
00634    struct ast_filestream *vfs;
00635    long sample_offset = 0;
00636    long max_length;
00637    int timeout = 0;
00638    char *edigits = "";
00639 
00640    if ( argc < 4 || argc > 5 )
00641       return RESULT_SHOWUSAGE;
00642 
00643    if ( argv[3] ) 
00644       edigits = argv[3];
00645 
00646    if ( argc == 5 )
00647       timeout = atoi(argv[4]);
00648    else if (chan->pbx->dtimeout) {
00649       /* by default dtimeout is set to 5sec */
00650       timeout = chan->pbx->dtimeout * 1000; /* in msec */
00651    }
00652 
00653    fs = ast_openstream(chan, argv[2], chan->language);
00654    if (!fs) {
00655       fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
00656       ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
00657       return RESULT_SUCCESS;
00658    }
00659    vfs = ast_openvstream(chan, argv[2], chan->language);
00660    if (vfs)
00661       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
00662    
00663    if (option_verbose > 2)
00664       ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
00665 
00666    ast_seekstream(fs, 0, SEEK_END);
00667    max_length = ast_tellstream(fs);
00668    ast_seekstream(fs, sample_offset, SEEK_SET);
00669    res = ast_applystream(chan, fs);
00670    if (vfs)
00671       vres = ast_applystream(chan, vfs);
00672    ast_playstream(fs);
00673    if (vfs)
00674       ast_playstream(vfs);
00675 
00676    res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
00677    /* this is to check for if ast_waitstream closed the stream, we probably are at
00678     * the end of the stream, return that amount, else check for the amount */
00679    sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
00680    ast_stopstream(chan);
00681    if (res == 1) {
00682       /* Stop this command, don't print a result line, as there is a new command */
00683       return RESULT_SUCCESS;
00684    }
00685 
00686    /* If the user didnt press a key, wait for digitTimeout*/
00687    if (res == 0 ) {
00688       res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
00689       /* Make sure the new result is in the escape digits of the GET OPTION */
00690       if ( !strchr(edigits,res) )
00691          res=0;
00692    }
00693 
00694         fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
00695    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00696 }
00697 
00698 
00699 
00700 
00701 /*--- handle_saynumber: Say number in various language syntaxes ---*/
00702 /* Need to add option for gender here as well. Coders wanted */
00703 /* While waiting, we're sending a (char *) NULL.  */
00704 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00705 {
00706    int res;
00707    int num;
00708    if (argc != 4)
00709       return RESULT_SHOWUSAGE;
00710    if (sscanf(argv[2], "%d", &num) != 1)
00711       return RESULT_SHOWUSAGE;
00712    res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
00713    if (res == 1)
00714       return RESULT_SUCCESS;
00715    fdprintf(agi->fd, "200 result=%d\n", res);
00716    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00717 }
00718 
00719 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00720 {
00721    int res;
00722    int num;
00723 
00724    if (argc != 4)
00725       return RESULT_SHOWUSAGE;
00726    if (sscanf(argv[2], "%d", &num) != 1)
00727       return RESULT_SHOWUSAGE;
00728 
00729    res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
00730    if (res == 1) /* New command */
00731       return RESULT_SUCCESS;
00732    fdprintf(agi->fd, "200 result=%d\n", res);
00733    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00734 }
00735 
00736 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00737 {
00738    int res;
00739 
00740    if (argc != 4)
00741       return RESULT_SHOWUSAGE;
00742 
00743    res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
00744    if (res == 1) /* New command */
00745       return RESULT_SUCCESS;
00746    fdprintf(agi->fd, "200 result=%d\n", res);
00747    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00748 }
00749 
00750 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00751 {
00752    int res;
00753    int num;
00754    if (argc != 4)
00755       return RESULT_SHOWUSAGE;
00756    if (sscanf(argv[2], "%d", &num) != 1)
00757       return RESULT_SHOWUSAGE;
00758    res = ast_say_date(chan, num, argv[3], chan->language);
00759    if (res == 1)
00760       return RESULT_SUCCESS;
00761    fdprintf(agi->fd, "200 result=%d\n", res);
00762    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00763 }
00764 
00765 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00766 {
00767    int res;
00768    int num;
00769    if (argc != 4)
00770       return RESULT_SHOWUSAGE;
00771    if (sscanf(argv[2], "%d", &num) != 1)
00772       return RESULT_SHOWUSAGE;
00773    res = ast_say_time(chan, num, argv[3], chan->language);
00774    if (res == 1)
00775       return RESULT_SUCCESS;
00776    fdprintf(agi->fd, "200 result=%d\n", res);
00777    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00778 }
00779 
00780 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00781 {
00782    int res=0;
00783    time_t unixtime;
00784    char *format, *zone=NULL;
00785    
00786    if (argc < 4)
00787       return RESULT_SHOWUSAGE;
00788 
00789    if (argc > 4) {
00790       format = argv[4];
00791    } else {
00792       /* XXX this doesn't belong here, but in the 'say' module */
00793       if (!strcasecmp(chan->language, "de")) {
00794          format = "A dBY HMS";
00795       } else {
00796          format = "ABdY 'digits/at' IMp"; 
00797       }
00798    }
00799 
00800    if (argc > 5 && !ast_strlen_zero(argv[5]))
00801       zone = argv[5];
00802 
00803    if (ast_get_time_t(argv[2], &unixtime, 0, NULL))
00804       return RESULT_SHOWUSAGE;
00805 
00806    res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone);
00807    if (res == 1)
00808       return RESULT_SUCCESS;
00809 
00810    fdprintf(agi->fd, "200 result=%d\n", res);
00811    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00812 }
00813 
00814 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00815 {
00816    int res;
00817 
00818    if (argc != 4)
00819       return RESULT_SHOWUSAGE;
00820 
00821    res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
00822    if (res == 1) /* New command */
00823       return RESULT_SUCCESS;
00824    fdprintf(agi->fd, "200 result=%d\n", res);
00825    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00826 }
00827 
00828 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00829 {
00830    int res;
00831    char data[1024];
00832    int max;
00833    int timeout;
00834 
00835    if (argc < 3)
00836       return RESULT_SHOWUSAGE;
00837    if (argc >= 4)
00838       timeout = atoi(argv[3]); 
00839    else
00840       timeout = 0;
00841    if (argc >= 5) 
00842       max = atoi(argv[4]); 
00843    else
00844       max = 1024;
00845    res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
00846    if (res == 2)        /* New command */
00847       return RESULT_SUCCESS;
00848    else if (res == 1)
00849       fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
00850    else if (res < 0 )
00851       fdprintf(agi->fd, "200 result=-1\n");
00852    else
00853       fdprintf(agi->fd, "200 result=%s\n", data);
00854    return RESULT_SUCCESS;
00855 }
00856 
00857 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00858 {
00859 
00860    if (argc != 3)
00861       return RESULT_SHOWUSAGE;
00862    ast_copy_string(chan->context, argv[2], sizeof(chan->context));
00863    fdprintf(agi->fd, "200 result=0\n");
00864    return RESULT_SUCCESS;
00865 }
00866    
00867 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00868 {
00869    if (argc != 3)
00870       return RESULT_SHOWUSAGE;
00871    ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
00872    fdprintf(agi->fd, "200 result=0\n");
00873    return RESULT_SUCCESS;
00874 }
00875 
00876 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00877 {
00878    int pri;
00879    if (argc != 3)
00880       return RESULT_SHOWUSAGE;   
00881 
00882    if (sscanf(argv[2], "%d", &pri) != 1) {
00883       if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
00884          return RESULT_SHOWUSAGE;
00885    }
00886 
00887    ast_explicit_goto(chan, NULL, NULL, pri);
00888    fdprintf(agi->fd, "200 result=0\n");
00889    return RESULT_SUCCESS;
00890 }
00891       
00892 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00893 {
00894    struct ast_filestream *fs;
00895    struct ast_frame *f;
00896    struct timeval start;
00897    long sample_offset = 0;
00898    int res = 0;
00899    int ms;
00900 
00901         struct ast_dsp *sildet=NULL;         /* silence detector dsp */
00902         int totalsilence = 0;
00903         int dspsilence = 0;
00904         int silence = 0;                /* amount of silence to allow */
00905         int gotsilence = 0;             /* did we timeout for silence? */
00906         char *silencestr=NULL;
00907         int rfmt=0;
00908 
00909 
00910    /* XXX EAGI FIXME XXX */
00911 
00912    if (argc < 6)
00913       return RESULT_SHOWUSAGE;
00914    if (sscanf(argv[5], "%d", &ms) != 1)
00915       return RESULT_SHOWUSAGE;
00916 
00917    if (argc > 6)
00918       silencestr = strchr(argv[6],'s');
00919    if ((argc > 7) && (!silencestr))
00920       silencestr = strchr(argv[7],'s');
00921    if ((argc > 8) && (!silencestr))
00922       silencestr = strchr(argv[8],'s');
00923 
00924    if (silencestr) {
00925       if (strlen(silencestr) > 2) {
00926          if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
00927             silencestr++;
00928             silencestr++;
00929             if (silencestr)
00930                         silence = atoi(silencestr);
00931                if (silence > 0)
00932                         silence *= 1000;
00933             }
00934       }
00935    }
00936 
00937         if (silence > 0) {
00938          rfmt = chan->readformat;
00939                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00940                 if (res < 0) {
00941                   ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00942                         return -1;
00943                 }
00944                   sildet = ast_dsp_new();
00945                 if (!sildet) {
00946                   ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00947                         return -1;
00948                 }
00949                   ast_dsp_set_threshold(sildet, 256);
00950          }
00951 
00952    /* backward compatibility, if no offset given, arg[6] would have been
00953     * caught below and taken to be a beep, else if it is a digit then it is a
00954     * offset */
00955    if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
00956       res = ast_streamfile(chan, "beep", chan->language);
00957 
00958    if ((argc > 7) && (!strchr(argv[7], '=')))
00959       res = ast_streamfile(chan, "beep", chan->language);
00960 
00961    if (!res)
00962       res = ast_waitstream(chan, argv[4]);
00963    if (res) {
00964       fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
00965    } else {
00966       fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
00967       if (!fs) {
00968          res = -1;
00969          fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
00970          if (sildet)
00971             ast_dsp_free(sildet);
00972          return RESULT_FAILURE;
00973       }
00974       
00975       /* Request a video update */
00976       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00977    
00978       chan->stream = fs;
00979       ast_applystream(chan,fs);
00980       /* really should have checks */
00981       ast_seekstream(fs, sample_offset, SEEK_SET);
00982       ast_truncstream(fs);
00983       
00984       start = ast_tvnow();
00985       while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
00986          res = ast_waitfor(chan, -1);
00987          if (res < 0) {
00988             ast_closestream(fs);
00989             fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
00990             if (sildet)
00991                ast_dsp_free(sildet);
00992             return RESULT_FAILURE;
00993          }
00994          f = ast_read(chan);
00995          if (!f) {
00996             fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
00997             ast_closestream(fs);
00998             if (sildet)
00999                ast_dsp_free(sildet);
01000             return RESULT_FAILURE;
01001          }
01002          switch(f->frametype) {
01003          case AST_FRAME_DTMF:
01004             if (strchr(argv[4], f->subclass)) {
01005                /* This is an interrupting chracter, so rewind to chop off any small
01006                   amount of DTMF that may have been recorded
01007                */
01008                ast_stream_rewind(fs, 200);
01009                ast_truncstream(fs);
01010                sample_offset = ast_tellstream(fs);
01011                fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
01012                ast_closestream(fs);
01013                ast_frfree(f);
01014                if (sildet)
01015                   ast_dsp_free(sildet);
01016                return RESULT_SUCCESS;
01017             }
01018             break;
01019          case AST_FRAME_VOICE:
01020             ast_writestream(fs, f);
01021             /* this is a safe place to check progress since we know that fs
01022              * is valid after a write, and it will then have our current
01023              * location */
01024             sample_offset = ast_tellstream(fs);
01025                                 if (silence > 0) {
01026                                  dspsilence = 0;
01027                                         ast_dsp_silence(sildet, f, &dspsilence);
01028                                         if (dspsilence) {
01029                                              totalsilence = dspsilence;
01030                                         } else {
01031                                                 totalsilence = 0;
01032                                         }
01033                                         if (totalsilence > silence) {
01034                                              /* Ended happily with silence */
01035                                                 gotsilence = 1;
01036                                                 break;
01037                                         }
01038                               }
01039             break;
01040          case AST_FRAME_VIDEO:
01041             ast_writestream(fs, f);
01042          default:
01043             /* Ignore all other frames */
01044             break;
01045          }
01046          ast_frfree(f);
01047          if (gotsilence)
01048             break;
01049          }
01050 
01051                if (gotsilence) {
01052                         ast_stream_rewind(fs, silence-1000);
01053                   ast_truncstream(fs);
01054          sample_offset = ast_tellstream(fs);
01055       }     
01056       fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
01057       ast_closestream(fs);
01058    }
01059 
01060         if (silence > 0) {
01061                 res = ast_set_read_format(chan, rfmt);
01062                 if (res)
01063                         ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
01064                 ast_dsp_free(sildet);
01065         }
01066    return RESULT_SUCCESS;
01067 }
01068 
01069 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01070 {
01071    int timeout;
01072 
01073    if (argc != 3)
01074       return RESULT_SHOWUSAGE;
01075    if (sscanf(argv[2], "%d", &timeout) != 1)
01076       return RESULT_SHOWUSAGE;
01077    if (timeout < 0)
01078       timeout = 0;
01079    if (timeout)
01080       chan->whentohangup = time(NULL) + timeout;
01081    else
01082       chan->whentohangup = 0;
01083    fdprintf(agi->fd, "200 result=0\n");
01084    return RESULT_SUCCESS;
01085 }
01086 
01087 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01088 {
01089    struct ast_channel *c;
01090    if (argc == 1) {
01091       /* no argument: hangup the current channel */
01092       ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
01093       fdprintf(agi->fd, "200 result=1\n");
01094       return RESULT_SUCCESS;
01095    } else if (argc == 2) {
01096       /* one argument: look for info on the specified channel */
01097       c = ast_get_channel_by_name_locked(argv[1]);
01098       if (c) {
01099          /* we have a matching channel */
01100          ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
01101          fdprintf(agi->fd, "200 result=1\n");
01102          ast_channel_unlock(c);
01103          return RESULT_SUCCESS;
01104       }
01105       /* if we get this far no channel name matched the argument given */
01106       fdprintf(agi->fd, "200 result=-1\n");
01107       return RESULT_SUCCESS;
01108    } else {
01109       return RESULT_SHOWUSAGE;
01110    }
01111 }
01112 
01113 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01114 {
01115    int res;
01116    struct ast_app *app;
01117 
01118    if (argc < 2)
01119       return RESULT_SHOWUSAGE;
01120 
01121    if (option_verbose > 2)
01122       ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
01123 
01124    app = pbx_findapp(argv[1]);
01125 
01126    if (app) {
01127       res = pbx_exec(chan, app, argv[2]);
01128    } else {
01129       ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01130       res = -2;
01131    }
01132    fdprintf(agi->fd, "200 result=%d\n", res);
01133 
01134    /* Even though this is wrong, users are depending upon this result. */
01135    return res;
01136 }
01137 
01138 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01139 {
01140    char tmp[256]="";
01141    char *l = NULL, *n = NULL;
01142 
01143    if (argv[2]) {
01144       ast_copy_string(tmp, argv[2], sizeof(tmp));
01145       ast_callerid_parse(tmp, &n, &l);
01146       if (l)
01147          ast_shrink_phone_number(l);
01148       else
01149          l = "";
01150       if (!n)
01151          n = "";
01152       ast_set_callerid(chan, l, n, NULL);
01153    }
01154 
01155    fdprintf(agi->fd, "200 result=1\n");
01156    return RESULT_SUCCESS;
01157 }
01158 
01159 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01160 {
01161    struct ast_channel *c;
01162    if (argc == 2) {
01163       /* no argument: supply info on the current channel */
01164       fdprintf(agi->fd, "200 result=%d\n", chan->_state);
01165       return RESULT_SUCCESS;
01166    } else if (argc == 3) {
01167       /* one argument: look for info on the specified channel */
01168       c = ast_get_channel_by_name_locked(argv[2]);
01169       if (c) {
01170          fdprintf(agi->fd, "200 result=%d\n", c->_state);
01171          ast_channel_unlock(c);
01172          return RESULT_SUCCESS;
01173       }
01174       /* if we get this far no channel name matched the argument given */
01175       fdprintf(agi->fd, "200 result=-1\n");
01176       return RESULT_SUCCESS;
01177    } else {
01178       return RESULT_SHOWUSAGE;
01179    }
01180 }
01181 
01182 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01183 {
01184    if (argv[3])
01185       pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01186 
01187    fdprintf(agi->fd, "200 result=1\n");
01188    return RESULT_SUCCESS;
01189 }
01190 
01191 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01192 {
01193    char *ret;
01194    char tempstr[1024];
01195 
01196    if (argc != 3)
01197       return RESULT_SHOWUSAGE;
01198 
01199    /* check if we want to execute an ast_custom_function */
01200    if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01201       ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01202    } else {
01203       pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01204    }
01205 
01206    if (ret)
01207       fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
01208    else
01209       fdprintf(agi->fd, "200 result=0\n");
01210 
01211    return RESULT_SUCCESS;
01212 }
01213 
01214 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01215 {
01216    char tmp[4096] = "";
01217    struct ast_channel *chan2=NULL;
01218 
01219    if ((argc != 4) && (argc != 5))
01220       return RESULT_SHOWUSAGE;
01221    if (argc == 5) {
01222       chan2 = ast_get_channel_by_name_locked(argv[4]);
01223    } else {
01224       chan2 = chan;
01225    }
01226    if (chan) { /* XXX isn't this chan2 ? */
01227       pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01228       fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
01229    } else {
01230       fdprintf(agi->fd, "200 result=0\n");
01231    }
01232    if (chan2 && (chan2 != chan))
01233       ast_channel_unlock(chan2);
01234    return RESULT_SUCCESS;
01235 }
01236 
01237 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01238 {
01239    int level = 0;
01240    char *prefix;
01241 
01242    if (argc < 2)
01243       return RESULT_SHOWUSAGE;
01244 
01245    if (argv[2])
01246       sscanf(argv[2], "%d", &level);
01247 
01248    switch (level) {
01249       case 4:
01250          prefix = VERBOSE_PREFIX_4;
01251          break;
01252       case 3:
01253          prefix = VERBOSE_PREFIX_3;
01254          break;
01255       case 2:
01256          prefix = VERBOSE_PREFIX_2;
01257          break;
01258       case 1:
01259       default:
01260          prefix = VERBOSE_PREFIX_1;
01261          break;
01262    }
01263 
01264    if (level <= option_verbose)
01265       ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
01266    
01267    fdprintf(agi->fd, "200 result=1\n");
01268    
01269    return RESULT_SUCCESS;
01270 }
01271 
01272 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01273 {
01274    int res;
01275    char tmp[256];
01276 
01277    if (argc != 4)
01278       return RESULT_SHOWUSAGE;
01279    res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
01280    if (res) 
01281       fdprintf(agi->fd, "200 result=0\n");
01282    else
01283       fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
01284 
01285    return RESULT_SUCCESS;
01286 }
01287 
01288 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01289 {
01290    int res;
01291 
01292    if (argc != 5)
01293       return RESULT_SHOWUSAGE;
01294    res = ast_db_put(argv[2], argv[3], argv[4]);
01295    fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01296    return RESULT_SUCCESS;
01297 }
01298 
01299 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01300 {
01301    int res;
01302 
01303    if (argc != 4)
01304       return RESULT_SHOWUSAGE;
01305    res = ast_db_del(argv[2], argv[3]);
01306    fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01307    return RESULT_SUCCESS;
01308 }
01309 
01310 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01311 {
01312    int res;
01313    if ((argc < 3) || (argc > 4))
01314       return RESULT_SHOWUSAGE;
01315    if (argc == 4)
01316       res = ast_db_deltree(argv[2], argv[3]);
01317    else
01318       res = ast_db_deltree(argv[2], NULL);
01319 
01320    fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01321    return RESULT_SUCCESS;
01322 }
01323 
01324 static char debug_usage[] = 
01325 "Usage: agi debug\n"
01326 "       Enables dumping of AGI transactions for debugging purposes\n";
01327 
01328 static char no_debug_usage[] = 
01329 "Usage: agi debug off\n"
01330 "       Disables dumping of AGI transactions for debugging purposes\n";
01331 
01332 static int agi_do_debug(int fd, int argc, char *argv[])
01333 {
01334    if (argc != 2)
01335       return RESULT_SHOWUSAGE;
01336    agidebug = 1;
01337    ast_cli(fd, "AGI Debugging Enabled\n");
01338    return RESULT_SUCCESS;
01339 }
01340 
01341 static int agi_no_debug_deprecated(int fd, int argc, char *argv[])
01342 {
01343    if (argc != 3)
01344       return RESULT_SHOWUSAGE;
01345    agidebug = 0;
01346    ast_cli(fd, "AGI Debugging Disabled\n");
01347    return RESULT_SUCCESS;
01348 }
01349 
01350 static int agi_no_debug(int fd, int argc, char *argv[])
01351 {
01352    if (argc != 3)
01353       return RESULT_SHOWUSAGE;
01354    agidebug = 0;
01355    ast_cli(fd, "AGI Debugging Disabled\n");
01356    return RESULT_SUCCESS;
01357 }
01358 
01359 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01360 {
01361    fdprintf(agi->fd, "200 result=0\n");
01362    return RESULT_SUCCESS;
01363 }
01364 
01365 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01366 {
01367    if (!strncasecmp(argv[2], "on", 2))
01368       ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01369    else if (!strncasecmp(argv[2], "off", 3))
01370       ast_moh_stop(chan);
01371    fdprintf(agi->fd, "200 result=0\n");
01372    return RESULT_SUCCESS;
01373 }
01374 
01375 static char usage_setmusic[] =
01376 " Usage: SET MUSIC ON <on|off> <class>\n"
01377 "  Enables/Disables the music on hold generator.  If <class> is\n"
01378 " not specified, then the default music on hold class will be used.\n"
01379 " Always returns 0.\n";
01380 
01381 static char usage_dbput[] =
01382 " Usage: DATABASE PUT <family> <key> <value>\n"
01383 "  Adds or updates an entry in the Asterisk database for a\n"
01384 " given family, key, and value.\n"
01385 " Returns 1 if successful, 0 otherwise.\n";
01386 
01387 static char usage_dbget[] =
01388 " Usage: DATABASE GET <family> <key>\n"
01389 "  Retrieves an entry in the Asterisk database for a\n"
01390 " given family and key.\n"
01391 " Returns 0 if <key> is not set.  Returns 1 if <key>\n"
01392 " is set and returns the variable in parentheses.\n"
01393 " Example return code: 200 result=1 (testvariable)\n";
01394 
01395 static char usage_dbdel[] =
01396 " Usage: DATABASE DEL <family> <key>\n"
01397 "  Deletes an entry in the Asterisk database for a\n"
01398 " given family and key.\n"
01399 " Returns 1 if successful, 0 otherwise.\n";
01400 
01401 static char usage_dbdeltree[] =
01402 " Usage: DATABASE DELTREE <family> [keytree]\n"
01403 "  Deletes a family or specific keytree within a family\n"
01404 " in the Asterisk database.\n"
01405 " Returns 1 if successful, 0 otherwise.\n";
01406 
01407 static char usage_verbose[] =
01408 " Usage: VERBOSE <message> <level>\n"
01409 "  Sends <message> to the console via verbose message system.\n"
01410 " <level> is the the verbose level (1-4)\n"
01411 " Always returns 1.\n";
01412 
01413 static char usage_getvariable[] =
01414 " Usage: GET VARIABLE <variablename>\n"
01415 "  Returns 0 if <variablename> is not set.  Returns 1 if <variablename>\n"
01416 " is set and returns the variable in parentheses.\n"
01417 " example return code: 200 result=1 (testvariable)\n";
01418 
01419 static char usage_getvariablefull[] =
01420 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
01421 "  Returns 0 if <variablename> is not set or channel does not exist.  Returns 1\n"
01422 "if <variablename>  is set and returns the variable in parenthesis.  Understands\n"
01423 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
01424 " example return code: 200 result=1 (testvariable)\n";
01425 
01426 static char usage_setvariable[] =
01427 " Usage: SET VARIABLE <variablename> <value>\n";
01428 
01429 static char usage_channelstatus[] =
01430 " Usage: CHANNEL STATUS [<channelname>]\n"
01431 "  Returns the status of the specified channel.\n" 
01432 " If no channel name is given the returns the status of the\n"
01433 " current channel.  Return values:\n"
01434 "  0 Channel is down and available\n"
01435 "  1 Channel is down, but reserved\n"
01436 "  2 Channel is off hook\n"
01437 "  3 Digits (or equivalent) have been dialed\n"
01438 "  4 Line is ringing\n"
01439 "  5 Remote end is ringing\n"
01440 "  6 Line is up\n"
01441 "  7 Line is busy\n";
01442 
01443 static char usage_setcallerid[] =
01444 " Usage: SET CALLERID <number>\n"
01445 "  Changes the callerid of the current channel.\n";
01446 
01447 static char usage_exec[] =
01448 " Usage: EXEC <application> <options>\n"
01449 "  Executes <application> with given <options>.\n"
01450 " Returns whatever the application returns, or -2 on failure to find application\n";
01451 
01452 static char usage_hangup[] =
01453 " Usage: HANGUP [<channelname>]\n"
01454 "  Hangs up the specified channel.\n"
01455 " If no channel name is given, hangs up the current channel\n";
01456 
01457 static char usage_answer[] = 
01458 " Usage: ANSWER\n"
01459 "  Answers channel if not already in answer state. Returns -1 on\n"
01460 " channel failure, or 0 if successful.\n";
01461 
01462 static char usage_waitfordigit[] = 
01463 " Usage: WAIT FOR DIGIT <timeout>\n"
01464 "  Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
01465 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
01466 " the numerical value of the ascii of the digit if one is received.  Use -1\n"
01467 " for the timeout value if you desire the call to block indefinitely.\n";
01468 
01469 static char usage_sendtext[] =
01470 " Usage: SEND TEXT \"<text to send>\"\n"
01471 "  Sends the given text on a channel. Most channels do not support the\n"
01472 " transmission of text.  Returns 0 if text is sent, or if the channel does not\n"
01473 " support text transmission.  Returns -1 only on error/hangup.  Text\n"
01474 " consisting of greater than one word should be placed in quotes since the\n"
01475 " command only accepts a single argument.\n";
01476 
01477 static char usage_recvchar[] =
01478 " Usage: RECEIVE CHAR <timeout>\n"
01479 "  Receives a character of text on a channel. Specify timeout to be the\n"
01480 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
01481 " do not support the reception of text. Returns the decimal value of the character\n"
01482 " if one is received, or 0 if the channel does not support text reception.  Returns\n"
01483 " -1 only on error/hangup.\n";
01484 
01485 static char usage_recvtext[] =
01486 " Usage: RECEIVE TEXT <timeout>\n"
01487 "  Receives a string of text on a channel. Specify timeout to be the\n"
01488 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
01489 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
01490 
01491 static char usage_tddmode[] =
01492 " Usage: TDD MODE <on|off>\n"
01493 "  Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
01494 " successful, or 0 if channel is not TDD-capable.\n";
01495 
01496 static char usage_sendimage[] =
01497 " Usage: SEND IMAGE <image>\n"
01498 "  Sends the given image on a channel. Most channels do not support the\n"
01499 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
01500 " support image transmission.  Returns -1 only on error/hangup. Image names\n"
01501 " should not include extensions.\n";
01502 
01503 static char usage_streamfile[] =
01504 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
01505 "  Send the given file, allowing playback to be interrupted by the given\n"
01506 " digits, if any. Use double quotes for the digits if you wish none to be\n"
01507 " permitted. If sample offset is provided then the audio will seek to sample\n"
01508 " offset before play starts.  Returns 0 if playback completes without a digit\n"
01509 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
01510 " or -1 on error or if the channel was disconnected. Remember, the file\n"
01511 " extension must not be included in the filename.\n";
01512 
01513 static char usage_controlstreamfile[] =
01514 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
01515 "  Send the given file, allowing playback to be controled by the given\n"
01516 " digits, if any. Use double quotes for the digits if you wish none to be\n"
01517 " permitted.  Returns 0 if playback completes without a digit\n"
01518 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
01519 " or -1 on error or if the channel was disconnected. Remember, the file\n"
01520 " extension must not be included in the filename.\n\n"
01521 " Note: ffchar and rewchar default to * and # respectively.\n";
01522 
01523 static char usage_getoption[] = 
01524 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
01525 "  Behaves similar to STREAM FILE but used with a timeout option.\n";
01526 
01527 static char usage_saynumber[] =
01528 " Usage: SAY NUMBER <number> <escape digits>\n"
01529 "  Say a given number, returning early if any of the given DTMF digits\n"
01530 " are received on the channel.  Returns 0 if playback completes without a digit\n"
01531 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01532 " -1 on error/hangup.\n";
01533 
01534 static char usage_saydigits[] =
01535 " Usage: SAY DIGITS <number> <escape digits>\n"
01536 "  Say a given digit string, returning early if any of the given DTMF digits\n"
01537 " are received on the channel. Returns 0 if playback completes without a digit\n"
01538 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01539 " -1 on error/hangup.\n";
01540 
01541 static char usage_sayalpha[] =
01542 " Usage: SAY ALPHA <number> <escape digits>\n"
01543 "  Say a given character string, returning early if any of the given DTMF digits\n"
01544 " are received on the channel. Returns 0 if playback completes without a digit\n"
01545 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01546 " -1 on error/hangup.\n";
01547 
01548 static char usage_saydate[] =
01549 " Usage: SAY DATE <date> <escape digits>\n"
01550 "  Say a given date, returning early if any of the given DTMF digits are\n"
01551 " received on the channel.  <date> is number of seconds elapsed since 00:00:00\n"
01552 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
01553 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01554 " digit if one was pressed or -1 on error/hangup.\n";
01555 
01556 static char usage_saytime[] =
01557 " Usage: SAY TIME <time> <escape digits>\n"
01558 "  Say a given time, returning early if any of the given DTMF digits are\n"
01559 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
01560 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
01561 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01562 " digit if one was pressed or -1 on error/hangup.\n";
01563 
01564 static char usage_saydatetime[] =
01565 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
01566 "  Say a given time, returning early if any of the given DTMF digits are\n"
01567 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
01568 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
01569 " the time should be said in.  See voicemail.conf (defaults to \"ABdY\n"
01570 " 'digits/at' IMp\").  Acceptable values for [timezone] can be found in\n"
01571 " /usr/share/zoneinfo.  Defaults to machine default. Returns 0 if playback\n"
01572 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01573 " digit if one was pressed or -1 on error/hangup.\n";
01574 
01575 static char usage_sayphonetic[] =
01576 " Usage: SAY PHONETIC <string> <escape digits>\n"
01577 "  Say a given character string with phonetics, returning early if any of the\n"
01578 " given DTMF digits are received on the channel. Returns 0 if playback\n"
01579 " completes without a digit pressed, the ASCII numerical value of the digit\n"
01580 " if one was pressed, or -1 on error/hangup.\n";
01581 
01582 static char usage_getdata[] =
01583 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
01584 "  Stream the given file, and recieve DTMF data. Returns the digits received\n"
01585 "from the channel at the other end.\n";
01586 
01587 static char usage_setcontext[] =
01588 " Usage: SET CONTEXT <desired context>\n"
01589 "  Sets the context for continuation upon exiting the application.\n";
01590 
01591 static char usage_setextension[] =
01592 " Usage: SET EXTENSION <new extension>\n"
01593 "  Changes the extension for continuation upon exiting the application.\n";
01594 
01595 static char usage_setpriority[] =
01596 " Usage: SET PRIORITY <priority>\n"
01597 "  Changes the priority for continuation upon exiting the application.\n"
01598 " The priority must be a valid priority or label.\n";
01599 
01600 static char usage_recordfile[] =
01601 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
01602 "                                          [offset samples] [BEEP] [s=silence]\n"
01603 "  Record to a file until a given dtmf digit in the sequence is received\n"
01604 " Returns -1 on hangup or error.  The format will specify what kind of file\n"
01605 " will be recorded.  The timeout is the maximum record time in milliseconds, or\n"
01606 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
01607 " to the offset without exceeding the end of the file.  \"silence\" is the number\n"
01608 " of seconds of silence allowed before the function returns despite the\n"
01609 " lack of dtmf digits or reaching timeout.  Silence value must be\n"
01610 " preceeded by \"s=\" and is also optional.\n";
01611 
01612 static char usage_autohangup[] =
01613 " Usage: SET AUTOHANGUP <time>\n"
01614 "  Cause the channel to automatically hangup at <time> seconds in the\n"
01615 " future.  Of course it can be hungup before then as well. Setting to 0 will\n"
01616 " cause the autohangup feature to be disabled on this channel.\n";
01617 
01618 static char usage_noop[] =
01619 " Usage: NoOp\n"
01620 "  Does nothing.\n";
01621 
01622 static agi_command commands[MAX_COMMANDS] = {
01623    { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
01624    { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
01625    { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
01626    { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
01627    { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
01628    { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
01629    { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
01630    { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
01631    { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
01632    { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
01633    { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
01634    { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
01635    { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
01636    { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar },
01637    { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext },
01638    { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
01639    { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
01640    { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
01641    { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
01642    { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
01643    { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate },
01644    { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
01645    { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime },
01646    { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
01647    { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
01648    { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
01649    { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
01650    { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
01651    { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
01652    { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
01653    { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
01654    { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
01655    { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
01656    { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile },
01657    { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
01658    { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
01659    { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
01660 };
01661 
01662 static int help_workhorse(int fd, char *match[])
01663 {
01664    char fullcmd[80];
01665    char matchstr[80];
01666    int x;
01667    struct agi_command *e;
01668    if (match)
01669       ast_join(matchstr, sizeof(matchstr), match);
01670    for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
01671       e = &commands[x]; 
01672       if (!e->cmda[0])
01673          break;
01674       /* Hide commands that start with '_' */
01675       if ((e->cmda[0])[0] == '_')
01676          continue;
01677       ast_join(fullcmd, sizeof(fullcmd), e->cmda);
01678       if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
01679          continue;
01680       ast_cli(fd, "%20.20s   %s\n", fullcmd, e->summary);
01681    }
01682    return 0;
01683 }
01684 
01685 int ast_agi_register(agi_command *agi)
01686 {
01687    int x;
01688    for (x=0; x<MAX_COMMANDS - 1; x++) {
01689       if (commands[x].cmda[0] == agi->cmda[0]) {
01690          ast_log(LOG_WARNING, "Command already registered!\n");
01691          return -1;
01692       }
01693    }
01694    for (x=0; x<MAX_COMMANDS - 1; x++) {
01695       if (!commands[x].cmda[0]) {
01696          commands[x] = *agi;
01697          return 0;
01698       }
01699    }
01700    ast_log(LOG_WARNING, "No more room for new commands!\n");
01701    return -1;
01702 }
01703 
01704 void ast_agi_unregister(agi_command *agi)
01705 {
01706    int x;
01707    for (x=0; x<MAX_COMMANDS - 1; x++) {
01708       if (commands[x].cmda[0] == agi->cmda[0]) {
01709          memset(&commands[x], 0, sizeof(agi_command));
01710       }
01711    }
01712 }
01713 
01714 static agi_command *find_command(char *cmds[], int exact)
01715 {
01716    int x;
01717    int y;
01718    int match;
01719 
01720    for (x=0; x < sizeof(commands) / sizeof(commands[0]); x++) {
01721       if (!commands[x].cmda[0])
01722          break;
01723       /* start optimistic */
01724       match = 1;
01725       for (y=0; match && cmds[y]; y++) {
01726          /* If there are no more words in the command (and we're looking for
01727             an exact match) or there is a difference between the two words,
01728             then this is not a match */
01729          if (!commands[x].cmda[y] && !exact)
01730             break;
01731          /* don't segfault if the next part of a command doesn't exist */
01732          if (!commands[x].cmda[y])
01733             return NULL;
01734          if (strcasecmp(commands[x].cmda[y], cmds[y]))
01735             match = 0;
01736       }
01737       /* If more words are needed to complete the command then this is not
01738          a candidate (unless we're looking for a really inexact answer  */
01739       if ((exact > -1) && commands[x].cmda[y])
01740          match = 0;
01741       if (match)
01742          return &commands[x];
01743    }
01744    return NULL;
01745 }
01746 
01747 
01748 static int parse_args(char *s, int *max, char *argv[])
01749 {
01750    int x=0;
01751    int quoted=0;
01752    int escaped=0;
01753    int whitespace=1;
01754    char *cur;
01755 
01756    cur = s;
01757    while(*s) {
01758       switch(*s) {
01759       case '"':
01760          /* If it's escaped, put a literal quote */
01761          if (escaped) 
01762             goto normal;
01763          else 
01764             quoted = !quoted;
01765          if (quoted && whitespace) {
01766             /* If we're starting a quote, coming off white space start a new word, too */
01767             argv[x++] = cur;
01768             whitespace=0;
01769          }
01770          escaped = 0;
01771       break;
01772       case ' ':
01773       case '\t':
01774          if (!quoted && !escaped) {
01775             /* If we're not quoted, mark this as whitespace, and
01776                end the previous argument */
01777             whitespace = 1;
01778             *(cur++) = '\0';
01779          } else
01780             /* Otherwise, just treat it as anything else */ 
01781             goto normal;
01782          break;
01783       case '\\':
01784          /* If we're escaped, print a literal, otherwise enable escaping */
01785          if (escaped) {
01786             goto normal;
01787          } else {
01788             escaped=1;
01789          }
01790          break;
01791       default:
01792 normal:
01793          if (whitespace) {
01794             if (x >= MAX_ARGS -1) {
01795                ast_log(LOG_WARNING, "Too many arguments, truncating\n");
01796                break;
01797             }
01798             /* Coming off of whitespace, start the next argument */
01799             argv[x++] = cur;
01800             whitespace=0;
01801          }
01802          *(cur++) = *s;
01803          escaped=0;
01804       }
01805       s++;
01806    }
01807    /* Null terminate */
01808    *(cur++) = '\0';
01809    argv[x] = NULL;
01810    *max = x;
01811    return 0;
01812 }
01813 
01814 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
01815 {
01816    char *argv[MAX_ARGS];
01817    int argc = MAX_ARGS;
01818    int res;
01819    agi_command *c;
01820 
01821    parse_args(buf, &argc, argv);
01822    c = find_command(argv, 0);
01823    if (c) {
01824       res = c->handler(chan, agi, argc, argv);
01825       switch(res) {
01826       case RESULT_SHOWUSAGE:
01827          fdprintf(agi->fd, "520-Invalid command syntax.  Proper usage follows:\n");
01828          fdprintf(agi->fd, c->usage);
01829          fdprintf(agi->fd, "520 End of proper usage.\n");
01830          break;
01831       case AST_PBX_KEEPALIVE:
01832          /* We've been asked to keep alive, so do so */
01833          return AST_PBX_KEEPALIVE;
01834          break;
01835       case RESULT_FAILURE:
01836          /* They've already given the failure.  We've been hung up on so handle this
01837             appropriately */
01838          return -1;
01839       }
01840    } else {
01841       fdprintf(agi->fd, "510 Invalid or unknown command\n");
01842    }
01843    return 0;
01844 }
01845 #define RETRY  3
01846 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
01847 {
01848    struct ast_channel *c;
01849    int outfd;
01850    int ms;
01851    enum agi_result returnstatus = AGI_RESULT_SUCCESS;
01852    struct ast_frame *f;
01853    char buf[2048];
01854    FILE *readf;
01855    /* how many times we'll retry if ast_waitfor_nandfs will return without either 
01856      channel or file descriptor in case select is interrupted by a system call (EINTR) */
01857    int retry = RETRY;
01858 
01859    if (!(readf = fdopen(agi->ctrl, "r"))) {
01860       ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
01861       if (pid > -1)
01862          kill(pid, SIGHUP);
01863       close(agi->ctrl);
01864       return AGI_RESULT_FAILURE;
01865    }
01866    setlinebuf(readf);
01867    setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
01868    for (;;) {
01869       ms = -1;
01870       c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
01871       if (c) {
01872          retry = RETRY;
01873          /* Idle the channel until we get a command */
01874          f = ast_read(c);
01875          if (!f) {
01876             ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
01877             returnstatus = AGI_RESULT_HANGUP;
01878             break;
01879          } else {
01880             /* If it's voice, write it to the audio pipe */
01881             if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
01882                /* Write, ignoring errors */
01883                write(agi->audio, f->data, f->datalen);
01884             }
01885             ast_frfree(f);
01886          }
01887       } else if (outfd > -1) {
01888          retry = RETRY;
01889          buf[0] = '\0';
01890          if (!fgets(buf, sizeof(buf), readf)) {
01891             /* Program terminated */
01892             if (returnstatus)
01893                returnstatus = -1;
01894             if (option_verbose > 2) 
01895                ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
01896             if (pid > 0)
01897                waitpid(pid, status, 0);
01898             /* No need to kill the pid anymore, since they closed us */
01899             pid = -1;
01900             break;
01901          }
01902          /* get rid of trailing newline, if any */
01903          if (*buf && buf[strlen(buf) - 1] == '\n')
01904             buf[strlen(buf) - 1] = 0;
01905          if (agidebug)
01906             ast_verbose("AGI Rx << %s\n", buf);
01907          returnstatus |= agi_handle_command(chan, agi, buf);
01908          /* If the handle_command returns -1, we need to stop */
01909          if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
01910             break;
01911          }
01912       } else {
01913          if (--retry <= 0) {
01914             ast_log(LOG_WARNING, "No channel, no fd?\n");
01915             returnstatus = AGI_RESULT_FAILURE;
01916             break;
01917          }
01918       }
01919    }
01920    /* Notify process */
01921    if (pid > -1) {
01922       const char *sighup = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
01923       if (ast_strlen_zero(sighup) || !ast_false(sighup)) {
01924          if (kill(pid, SIGHUP))
01925             ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
01926       }
01927    }
01928    fclose(readf);
01929    return returnstatus;
01930 }
01931 
01932 static int handle_showagi(int fd, int argc, char *argv[])
01933 {
01934    struct agi_command *e;
01935    char fullcmd[80];
01936    if ((argc < 2))
01937       return RESULT_SHOWUSAGE;
01938    if (argc > 2) {
01939       e = find_command(argv + 2, 1);
01940       if (e) 
01941          ast_cli(fd, e->usage);
01942       else {
01943          if (find_command(argv + 2, -1)) {
01944             return help_workhorse(fd, argv + 1);
01945          } else {
01946             ast_join(fullcmd, sizeof(fullcmd), argv+1);
01947             ast_cli(fd, "No such command '%s'.\n", fullcmd);
01948          }
01949       }
01950    } else {
01951       return help_workhorse(fd, NULL);
01952    }
01953    return RESULT_SUCCESS;
01954 }
01955 
01956 static int handle_agidumphtml(int fd, int argc, char *argv[])
01957 {
01958    struct agi_command *e;
01959    char fullcmd[80];
01960    int x;
01961    FILE *htmlfile;
01962 
01963    if ((argc < 3))
01964       return RESULT_SHOWUSAGE;
01965 
01966    if (!(htmlfile = fopen(argv[2], "wt"))) {
01967       ast_cli(fd, "Could not create file '%s'\n", argv[2]);
01968       return RESULT_SHOWUSAGE;
01969    }
01970 
01971    fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
01972    fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
01973 
01974 
01975    fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
01976 
01977    for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
01978       char *stringp, *tempstr;
01979 
01980       e = &commands[x]; 
01981       if (!e->cmda[0])  /* end ? */
01982          break;
01983       /* Hide commands that start with '_' */
01984       if ((e->cmda[0])[0] == '_')
01985          continue;
01986       ast_join(fullcmd, sizeof(fullcmd), e->cmda);
01987 
01988       fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
01989       fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd,e->summary);
01990 
01991       stringp=e->usage;
01992       tempstr = strsep(&stringp, "\n");
01993 
01994       fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
01995       
01996       fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
01997       while ((tempstr = strsep(&stringp, "\n")) != NULL)
01998          fprintf(htmlfile, "%s<BR>\n",tempstr);
01999       fprintf(htmlfile, "</TD></TR>\n");
02000       fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
02001 
02002    }
02003 
02004    fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
02005    fclose(htmlfile);
02006    ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
02007    return RESULT_SUCCESS;
02008 }
02009 
02010 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
02011 {
02012    enum agi_result res;
02013    struct ast_module_user *u;
02014    char *argv[MAX_ARGS];
02015    char buf[2048]="";
02016    char *tmp = (char *)buf;
02017    int argc = 0;
02018    int fds[2];
02019    int efd = -1;
02020    int pid;
02021         char *stringp;
02022    AGI agi;
02023 
02024    if (ast_strlen_zero(data)) {
02025       ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
02026       return -1;
02027    }
02028    ast_copy_string(buf, data, sizeof(buf));
02029 
02030    memset(&agi, 0, sizeof(agi));
02031         while ((stringp = strsep(&tmp, "|")) && argc < MAX_ARGS-1)
02032       argv[argc++] = stringp;
02033    argv[argc] = NULL;
02034 
02035    u = ast_module_user_add(chan);
02036 #if 0
02037     /* Answer if need be */
02038         if (chan->_state != AST_STATE_UP) {
02039       if (ast_answer(chan)) {
02040          LOCAL_USER_REMOVE(u);
02041          return -1;
02042       }
02043    }
02044 #endif
02045    res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
02046    if (res == AGI_RESULT_SUCCESS) {
02047       int status = 0;
02048       agi.fd = fds[1];
02049       agi.ctrl = fds[0];
02050       agi.audio = efd;
02051       res = run_agi(chan, argv[0], &agi, pid, &status, dead, argc, argv);
02052       /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
02053       if (res == AGI_RESULT_SUCCESS && status)
02054          res = AGI_RESULT_FAILURE;
02055       if (fds[1] != fds[0])
02056          close(fds[1]);
02057       if (efd > -1)
02058          close(efd);
02059       ast_unreplace_sigchld();
02060    }
02061    ast_module_user_remove(u);
02062 
02063    switch (res) {
02064    case AGI_RESULT_SUCCESS:
02065       pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
02066       break;
02067    case AGI_RESULT_FAILURE:
02068       pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
02069       break;
02070    case AGI_RESULT_HANGUP:
02071       pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
02072       return -1;
02073    }
02074 
02075    return 0;
02076 }
02077 
02078 static int agi_exec(struct ast_channel *chan, void *data)
02079 {
02080    if (chan->_softhangup)
02081       ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
02082    return agi_exec_full(chan, data, 0, 0);
02083 }
02084 
02085 static int eagi_exec(struct ast_channel *chan, void *data)
02086 {
02087    int readformat;
02088    int res;
02089 
02090    if (chan->_softhangup)
02091       ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
02092    readformat = chan->readformat;
02093    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
02094       ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
02095       return -1;
02096    }
02097    res = agi_exec_full(chan, data, 1, 0);
02098    if (!res) {
02099       if (ast_set_read_format(chan, readformat)) {
02100          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
02101       }
02102    }
02103    return res;
02104 }
02105 
02106 static int deadagi_exec(struct ast_channel *chan, void *data)
02107 {
02108    if (!ast_check_hangup(chan))
02109       ast_log(LOG_WARNING,"Running DeadAGI on a live channel will cause problems, please use AGI\n");
02110    return agi_exec_full(chan, data, 0, 1);
02111 }
02112 
02113 static char showagi_help[] =
02114 "Usage: agi show [topic]\n"
02115 "       When called with a topic as an argument, displays usage\n"
02116 "       information on the given command.  If called without a\n"
02117 "       topic, it provides a list of AGI commands.\n";
02118 
02119 
02120 static char dumpagihtml_help[] =
02121 "Usage: agi dumphtml <filename>\n"
02122 "  Dumps the agi command list in html format to given filename\n";
02123 
02124 static struct ast_cli_entry cli_show_agi_deprecated = {
02125    { "show", "agi", NULL },
02126    handle_showagi, NULL,
02127    NULL };
02128 
02129 static struct ast_cli_entry cli_dump_agihtml_deprecated = {
02130    { "dump", "agihtml", NULL },
02131    handle_agidumphtml, NULL,
02132    NULL };
02133 
02134 static struct ast_cli_entry cli_agi_no_debug_deprecated = {
02135    { "agi", "no", "debug", NULL },
02136    agi_no_debug_deprecated, NULL,
02137    NULL };
02138 
02139 static struct ast_cli_entry cli_agi[] = {
02140    { { "agi", "debug", NULL },
02141    agi_do_debug, "Enable AGI debugging",
02142    debug_usage },
02143 
02144    { { "agi", "debug", "off", NULL },
02145    agi_no_debug, "Disable AGI debugging",
02146    no_debug_usage, NULL, &cli_agi_no_debug_deprecated },
02147 
02148    { { "agi", "show", NULL },
02149    handle_showagi, "List AGI commands or specific help",
02150    showagi_help, NULL, &cli_show_agi_deprecated },
02151 
02152    { { "agi", "dumphtml", NULL },
02153    handle_agidumphtml, "Dumps a list of agi commands in html format",
02154    dumpagihtml_help, NULL, &cli_dump_agihtml_deprecated },
02155 };
02156 
02157 static int unload_module(void)
02158 {
02159    ast_module_user_hangup_all();
02160    ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
02161    ast_unregister_application(eapp);
02162    ast_unregister_application(deadapp);
02163    return ast_unregister_application(app);
02164 }
02165 
02166 static int load_module(void)
02167 {
02168    ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
02169    ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
02170    ast_register_application(eapp, eagi_exec, esynopsis, descrip);
02171    return ast_register_application(app, agi_exec, synopsis, descrip);
02172 }
02173 
02174 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
02175                 .load = load_module,
02176                 .unload = unload_module,
02177       );

Generated on Fri Aug 24 02:22:16 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1