Mon Mar 20 08:20:13 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Mon Mar 20 08:20:13 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1