Wed Mar 4 19:58:13 2009

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

Generated on Wed Mar 4 19:58:13 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7