Tue Nov 4 13:20:21 2008

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

Generated on Tue Nov 4 13:20:21 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7