Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:53 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen