Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:55 2009

## Asterisk developer's documentation

# res_agi.c

Go to the documentation of this file.

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