Mon May 14 04:42:57 2007

Asterisk developer's documentation


res_agi.c

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

Generated on Mon May 14 04:42:57 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1