Sat Nov 1 06:30:37 2008

Asterisk developer's documentation


res_agi.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief AGI - the Asterisk Gateway Interface
00022  * 
00023  */
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: 77782 $")
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], "%d", &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], "%d", &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], "%ld", &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], "%d", &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], "%d", &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], "%d", &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], "%d", &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], "%ld", &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], "%d", &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], "%d", &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], "%ld", &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 }