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

Generated on Wed Aug 15 01:24:23 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3