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

Generated on Fri Sep 25 19:28:14 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5