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

Generated on Mon Mar 31 07:38:04 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1