Sat Nov 1 06:28:35 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  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 146129 $")
00029 
00030 #include <sys/types.h>
00031 #include <netdb.h>
00032 #include <sys/socket.h>
00033 #include <netinet/in.h>
00034 #include <netinet/tcp.h>
00035 #include <arpa/inet.h>
00036 #include <math.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <string.h>
00040 #include <stdlib.h>
00041 #include <signal.h>
00042 #include <sys/time.h>
00043 #include <stdio.h>
00044 #include <fcntl.h>
00045 #include <errno.h>
00046 #include <sys/wait.h>
00047 
00048 #include "asterisk/file.h"
00049 #include "asterisk/logger.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/pbx.h"
00052 #include "asterisk/module.h"
00053 #include "asterisk/astdb.h"
00054 #include "asterisk/callerid.h"
00055 #include "asterisk/cli.h"
00056 #include "asterisk/logger.h"
00057 #include "asterisk/options.h"
00058 #include "asterisk/image.h"
00059 #include "asterisk/say.h"
00060 #include "asterisk/app.h"
00061 #include "asterisk/dsp.h"
00062 #include "asterisk/musiconhold.h"
00063 #include "asterisk/utils.h"
00064 #include "asterisk/lock.h"
00065 #include "asterisk/strings.h"
00066 #include "asterisk/agi.h"
00067 #include "asterisk/features.h"
00068 
00069 #define MAX_ARGS 128
00070 #define MAX_COMMANDS 128
00071 #define AGI_NANDFS_RETRY 3
00072 #define AGI_BUF_LEN 2048
00073 
00074 /* Recycle some stuff from the CLI interface */
00075 #define fdprintf agi_debug_cli
00076 
00077 static char *app = "AGI";
00078 
00079 static char *eapp = "EAGI";
00080 
00081 static char *deadapp = "DeadAGI";
00082 
00083 static char *synopsis = "Executes an AGI compliant application";
00084 static char *esynopsis = "Executes an EAGI compliant application";
00085 static char *deadsynopsis = "Executes AGI on a hungup channel";
00086 
00087 static char *descrip =
00088 "  [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
00089 "program on a channel. AGI allows Asterisk to launch external programs\n"
00090 "written in any language to control a telephony channel, play audio,\n"
00091 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
00092 "and stdout.\n"
00093 "  This channel will stop dialplan execution on hangup inside of this\n"
00094 "application, except when using DeadAGI.  Otherwise, dialplan execution\n"
00095 "will continue normally.\n"
00096 "  A locally executed AGI script will receive SIGHUP on hangup from the channel\n"
00097 "except when using DeadAGI. This can be disabled by setting the AGISIGHUP channel\n"
00098 "variable to \"no\" before executing the AGI application.\n"
00099 "  Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
00100 "on file descriptor 3\n\n"
00101 "  Use the CLI command 'agi show' to list available agi commands\n"
00102 "  This application sets the following channel variable upon completion:\n"
00103 "     AGISTATUS      The status of the attempt to the run the AGI script\n"
00104 "                    text string, one of SUCCESS | FAILURE | HANGUP\n";
00105 
00106 static int agidebug = 0;
00107 
00108 #define TONE_BLOCK_SIZE 200
00109 
00110 /* Max time to connect to an AGI remote host */
00111 #define MAX_AGI_CONNECT 2000
00112 
00113 #define AGI_PORT 4573
00114 
00115 enum agi_result {
00116    AGI_RESULT_FAILURE = -1,
00117    AGI_RESULT_SUCCESS,
00118    AGI_RESULT_SUCCESS_FAST,
00119    AGI_RESULT_HANGUP
00120 };
00121 
00122 static int agi_debug_cli(int fd, char *fmt, ...)
00123 {
00124    char *stuff;
00125    int res = 0;
00126 
00127    va_list ap;
00128    va_start(ap, fmt);
00129    res = vasprintf(&stuff, fmt, ap);
00130    va_end(ap);
00131    if (res == -1) {
00132       ast_log(LOG_ERROR, "Out of memory\n");
00133    } else {
00134       if (agidebug)
00135          ast_verbose("AGI Tx >> %s", stuff); /* \n provided by caller */
00136       res = ast_carefulwrite(fd, stuff, strlen(stuff), 100);
00137       free(stuff);
00138    }
00139 
00140    return res;
00141 }
00142 
00143 /* launch_netscript: The fastagi handler.
00144    FastAGI defaults to port 4573 */
00145 static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
00146 {
00147    int s;
00148    int flags;
00149    struct pollfd pfds[1];
00150    char *host;
00151    char *c; int port = AGI_PORT;
00152    char *script="";
00153    struct sockaddr_in sin;
00154    struct hostent *hp;
00155    struct ast_hostent ahp;
00156    int res;
00157 
00158    /* agiusl is "agi://host.domain[:port][/script/name]" */
00159    host = ast_strdupa(agiurl + 6);  /* Remove agi:// */
00160    /* Strip off any script name */
00161    if ((c = strchr(host, '/'))) {
00162       *c = '\0';
00163       c++;
00164       script = c;
00165    }
00166    if ((c = strchr(host, ':'))) {
00167       *c = '\0';
00168       c++;
00169       port = atoi(c);
00170    }
00171    if (efd) {
00172       ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
00173       return -1;
00174    }
00175    hp = ast_gethostbyname(host, &ahp);
00176    if (!hp) {
00177       ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
00178       return -1;
00179    }
00180    s = socket(AF_INET, SOCK_STREAM, 0);
00181    if (s < 0) {
00182       ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
00183       return -1;
00184    }
00185    flags = fcntl(s, F_GETFL);
00186    if (flags < 0) {
00187       ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
00188       close(s);
00189       return -1;
00190    }
00191    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
00192       ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
00193       close(s);
00194       return -1;
00195    }
00196    memset(&sin, 0, sizeof(sin));
00197    sin.sin_family = AF_INET;
00198    sin.sin_port = htons(port);
00199    memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
00200    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
00201       ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
00202       close(s);
00203       return AGI_RESULT_FAILURE;
00204    }
00205 
00206    pfds[0].fd = s;
00207    pfds[0].events = POLLOUT;
00208    while ((res = poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
00209       if (errno != EINTR) {
00210          if (!res) {
00211             ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
00212                agiurl, MAX_AGI_CONNECT);
00213          } else
00214             ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00215          close(s);
00216          return AGI_RESULT_FAILURE;
00217       }
00218    }
00219 
00220    if (fdprintf(s, "agi_network: yes\n") < 0) {
00221       if (errno != EINTR) {
00222          ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00223          close(s);
00224          return AGI_RESULT_FAILURE;
00225       }
00226    }
00227 
00228    /* If we have a script parameter, relay it to the fastagi server */
00229    if (!ast_strlen_zero(script))
00230       fdprintf(s, "agi_network_script: %s\n", script);
00231 
00232    if (option_debug > 3)
00233       ast_log(LOG_DEBUG, "Wow, connected!\n");
00234    fds[0] = s;
00235    fds[1] = s;
00236    *opid = -1;
00237    return AGI_RESULT_SUCCESS_FAST;
00238 }
00239 
00240 static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
00241 {
00242    char tmp[256];
00243    int pid;
00244    int toast[2];
00245    int fromast[2];
00246    int audio[2];
00247    int x;
00248    int res;
00249    sigset_t signal_set, old_set;
00250    
00251    if (!strncasecmp(script, "agi://", 6))
00252       return launch_netscript(script, argv, fds, efd, opid);
00253    
00254    if (script[0] != '/') {
00255       snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
00256       script = tmp;
00257    }
00258    if (pipe(toast)) {
00259       ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
00260       return AGI_RESULT_FAILURE;
00261    }
00262    if (pipe(fromast)) {
00263       ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
00264       close(toast[0]);
00265       close(toast[1]);
00266       return AGI_RESULT_FAILURE;
00267    }
00268    if (efd) {
00269       if (pipe(audio)) {
00270          ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
00271          close(fromast[0]);
00272          close(fromast[1]);
00273          close(toast[0]);
00274          close(toast[1]);
00275          return AGI_RESULT_FAILURE;
00276       }
00277       res = fcntl(audio[1], F_GETFL);
00278       if (res > -1) 
00279          res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
00280       if (res < 0) {
00281          ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
00282          close(fromast[0]);
00283          close(fromast[1]);
00284          close(toast[0]);
00285          close(toast[1]);
00286          close(audio[0]);
00287          close(audio[1]);
00288          return AGI_RESULT_FAILURE;
00289       }
00290    }
00291 
00292    /* Block SIGHUP during the fork - prevents a race */
00293    sigfillset(&signal_set);
00294    pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
00295    pid = fork();
00296    if (pid < 0) {
00297       ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00298       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00299       return AGI_RESULT_FAILURE;
00300    }
00301    if (!pid) {
00302       /* Pass paths to AGI via environmental variables */
00303       setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1);
00304       setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1);
00305       setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1);
00306       setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1);
00307       setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1);
00308       setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1);
00309       setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1);
00310       setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1);
00311       setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1);
00312       setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1);
00313       setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1);
00314 
00315       /* Don't run AGI scripts with realtime priority -- it causes audio stutter */
00316       ast_set_priority(0);
00317 
00318       /* Redirect stdin and out, provide enhanced audio channel if desired */
00319       dup2(fromast[0], STDIN_FILENO);
00320       dup2(toast[1], STDOUT_FILENO);
00321       if (efd) {
00322          dup2(audio[0], STDERR_FILENO + 1);
00323       } else {
00324          close(STDERR_FILENO + 1);
00325       }
00326 
00327       /* Before we unblock our signals, return our trapped signals back to the defaults */
00328       signal(SIGHUP, SIG_DFL);
00329       signal(SIGCHLD, SIG_DFL);
00330       signal(SIGINT, SIG_DFL);
00331       signal(SIGURG, SIG_DFL);
00332       signal(SIGTERM, SIG_DFL);
00333       signal(SIGPIPE, SIG_DFL);
00334       signal(SIGXFSZ, SIG_DFL);
00335 
00336       /* unblock important signal handlers */
00337       if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
00338          ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno));
00339          _exit(1);
00340       }
00341 
00342       /* Close everything but stdin/out/error */
00343       for (x=STDERR_FILENO + 2;x<1024;x++) 
00344          close(x);
00345 
00346       /* Execute script */
00347       execv(script, argv);
00348       /* Can't use ast_log since FD's are closed */
00349       fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno));
00350       /* Special case to set status of AGI to failure */
00351       fprintf(stdout, "failure\n");
00352       fflush(stdout);
00353       _exit(1);
00354    }
00355    pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00356    if (option_verbose > 2) 
00357       ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
00358    fds[0] = toast[0];
00359    fds[1] = fromast[1];
00360    if (efd) {
00361       *efd = audio[1];
00362    }
00363    /* close what we're not using in the parent */
00364    close(toast[1]);
00365    close(fromast[0]);
00366 
00367    if (efd)
00368       close(audio[0]);
00369 
00370    *opid = pid;
00371    return AGI_RESULT_SUCCESS;
00372 }
00373 
00374 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
00375 {
00376    /* Print initial environment, with agi_request always being the first
00377       thing */
00378    fdprintf(fd, "agi_request: %s\n", request);
00379    fdprintf(fd, "agi_channel: %s\n", chan->name);
00380    fdprintf(fd, "agi_language: %s\n", chan->language);
00381    fdprintf(fd, "agi_type: %s\n", chan->tech->type);
00382    fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
00383 
00384    /* ANI/DNIS */
00385    fdprintf(fd, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown"));
00386    fdprintf(fd, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown"));
00387    fdprintf(fd, "agi_callingpres: %d\n", chan->cid.cid_pres);
00388    fdprintf(fd, "agi_callingani2: %d\n", chan->cid.cid_ani2);
00389    fdprintf(fd, "agi_callington: %d\n", chan->cid.cid_ton);
00390    fdprintf(fd, "agi_callingtns: %d\n", chan->cid.cid_tns);
00391    fdprintf(fd, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown"));
00392    fdprintf(fd, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown"));
00393 
00394    /* Context information */
00395    fdprintf(fd, "agi_context: %s\n", chan->context);
00396    fdprintf(fd, "agi_extension: %s\n", chan->exten);
00397    fdprintf(fd, "agi_priority: %d\n", chan->priority);
00398    fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
00399 
00400    /* User information */
00401    fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
00402     
00403    /* End with empty return */
00404    fdprintf(fd, "\n");
00405 }
00406 
00407 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00408 {
00409    int res;
00410    res = 0;
00411    if (chan->_state != AST_STATE_UP) {
00412       /* Answer the chan */
00413       res = ast_answer(chan);
00414    }
00415    fdprintf(agi->fd, "200 result=%d\n", res);
00416    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00417 }
00418 
00419 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00420 {
00421    int res;
00422    int to;
00423    if (argc != 4)
00424       return RESULT_SHOWUSAGE;
00425    if (sscanf(argv[3], "%d", &to) != 1)
00426       return RESULT_SHOWUSAGE;
00427    res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
00428    fdprintf(agi->fd, "200 result=%d\n", res);
00429    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00430 }
00431 
00432 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00433 {
00434    int res;
00435    if (argc != 3)
00436       return RESULT_SHOWUSAGE;
00437    /* At the moment, the parser (perhaps broken) returns with
00438       the last argument PLUS the newline at the end of the input
00439       buffer. This probably needs to be fixed, but I wont do that
00440       because other stuff may break as a result. The right way
00441       would probably be to strip off the trailing newline before
00442       parsing, then here, add a newline at the end of the string
00443       before sending it to ast_sendtext --DUDE */
00444    res = ast_sendtext(chan, argv[2]);
00445    fdprintf(agi->fd, "200 result=%d\n", res);
00446    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00447 }
00448 
00449 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00450 {
00451    int res;
00452    if (argc != 3)
00453       return RESULT_SHOWUSAGE;
00454    res = ast_recvchar(chan,atoi(argv[2]));
00455    if (res == 0) {
00456       fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
00457       return RESULT_SUCCESS;
00458    }
00459    if (res > 0) {
00460       fdprintf(agi->fd, "200 result=%d\n", res);
00461       return RESULT_SUCCESS;
00462    }
00463    else {
00464       fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
00465       return RESULT_FAILURE;
00466    }
00467 }
00468 
00469 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00470 {
00471    char *buf;
00472    
00473    if (argc != 3)
00474       return RESULT_SHOWUSAGE;
00475    buf = ast_recvtext(chan,atoi(argv[2]));
00476    if (buf) {
00477       fdprintf(agi->fd, "200 result=1 (%s)\n", buf);
00478       free(buf);
00479    } else { 
00480       fdprintf(agi->fd, "200 result=-1\n");
00481    }
00482    return RESULT_SUCCESS;
00483 }
00484 
00485 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00486 {
00487    int res,x;
00488    if (argc != 3)
00489       return RESULT_SHOWUSAGE;
00490    if (!strncasecmp(argv[2],"on",2)) 
00491       x = 1; 
00492    else 
00493       x = 0;
00494    if (!strncasecmp(argv[2],"mate",4)) 
00495       x = 2;
00496    if (!strncasecmp(argv[2],"tdd",3))
00497       x = 1;
00498    res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
00499    if (res != RESULT_SUCCESS)
00500       fdprintf(agi->fd, "200 result=0\n");
00501    else
00502       fdprintf(agi->fd, "200 result=1\n");
00503    return RESULT_SUCCESS;
00504 }
00505 
00506 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00507 {
00508    int res;
00509    if (argc != 3)
00510       return RESULT_SHOWUSAGE;
00511    res = ast_send_image(chan, argv[2]);
00512    if (!ast_check_hangup(chan))
00513       res = 0;
00514    fdprintf(agi->fd, "200 result=%d\n", res);
00515    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00516 }
00517 
00518 static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00519 {
00520    int res = 0;
00521    int skipms = 3000;
00522    char *fwd = NULL;
00523    char *rev = NULL;
00524    char *pause = NULL;
00525    char *stop = NULL;
00526 
00527    if (argc < 5 || argc > 9)
00528       return RESULT_SHOWUSAGE;
00529 
00530    if (!ast_strlen_zero(argv[4]))
00531       stop = argv[4];
00532    else
00533       stop = NULL;
00534    
00535    if ((argc > 5) && (sscanf(argv[5], "%d", &skipms) != 1))
00536       return RESULT_SHOWUSAGE;
00537 
00538    if (argc > 6 && !ast_strlen_zero(argv[6]))
00539       fwd = argv[6];
00540    else
00541       fwd = "#";
00542 
00543    if (argc > 7 && !ast_strlen_zero(argv[7]))
00544       rev = argv[7];
00545    else
00546       rev = "*";
00547    
00548    if (argc > 8 && !ast_strlen_zero(argv[8]))
00549       pause = argv[8];
00550    else
00551       pause = NULL;
00552    
00553    res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, pause, NULL, skipms);
00554    
00555    fdprintf(agi->fd, "200 result=%d\n", res);
00556 
00557    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00558 }
00559 
00560 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00561 {
00562    int res;
00563    int vres;   
00564    struct ast_filestream *fs;
00565    struct ast_filestream *vfs;
00566    long sample_offset = 0;
00567    long max_length;
00568    char *edigits = "";
00569 
00570    if (argc < 4 || argc > 5)
00571       return RESULT_SHOWUSAGE;
00572 
00573    if (argv[3]) 
00574       edigits = argv[3];
00575 
00576    if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
00577       return RESULT_SHOWUSAGE;
00578    
00579    fs = ast_openstream(chan, argv[2], chan->language);   
00580    
00581    if (!fs) {
00582       fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
00583       return RESULT_SUCCESS;
00584    }  
00585    vfs = ast_openvstream(chan, argv[2], chan->language);
00586    if (vfs)
00587       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
00588       
00589    if (option_verbose > 2)
00590       ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
00591 
00592    ast_seekstream(fs, 0, SEEK_END);
00593    max_length = ast_tellstream(fs);
00594    ast_seekstream(fs, sample_offset, SEEK_SET);
00595    res = ast_applystream(chan, fs);
00596    if (vfs)
00597       vres = ast_applystream(chan, vfs);
00598    ast_playstream(fs);
00599    if (vfs)
00600       ast_playstream(vfs);
00601    
00602    res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
00603    /* this is to check for if ast_waitstream closed the stream, we probably are at
00604     * the end of the stream, return that amount, else check for the amount */
00605    sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
00606    ast_stopstream(chan);
00607    if (res == 1) {
00608       /* Stop this command, don't print a result line, as there is a new command */
00609       return RESULT_SUCCESS;
00610    }
00611    fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
00612    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00613 }
00614 
00615 /* get option - really similar to the handle_streamfile, but with a timeout */
00616 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00617 {
00618    int res;
00619    int vres;   
00620    struct ast_filestream *fs;
00621    struct ast_filestream *vfs;
00622    long sample_offset = 0;
00623    long max_length;
00624    int timeout = 0;
00625    char *edigits = "";
00626 
00627    if ( argc < 4 || argc > 5 )
00628       return RESULT_SHOWUSAGE;
00629 
00630    if ( argv[3] ) 
00631       edigits = argv[3];
00632 
00633    if ( argc == 5 )
00634       timeout = atoi(argv[4]);
00635    else if (chan->pbx->dtimeout) {
00636       /* by default dtimeout is set to 5sec */
00637       timeout = chan->pbx->dtimeout * 1000; /* in msec */
00638    }
00639 
00640    fs = ast_openstream(chan, argv[2], chan->language);
00641    if (!fs) {
00642       fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
00643       ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
00644       return RESULT_SUCCESS;
00645    }
00646    vfs = ast_openvstream(chan, argv[2], chan->language);
00647    if (vfs)
00648       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
00649    
00650    if (option_verbose > 2)
00651       ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
00652 
00653    ast_seekstream(fs, 0, SEEK_END);
00654    max_length = ast_tellstream(fs);
00655    ast_seekstream(fs, sample_offset, SEEK_SET);
00656    res = ast_applystream(chan, fs);
00657    if (vfs)
00658       vres = ast_applystream(chan, vfs);
00659    ast_playstream(fs);
00660    if (vfs)
00661       ast_playstream(vfs);
00662 
00663    res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
00664    /* this is to check for if ast_waitstream closed the stream, we probably are at
00665     * the end of the stream, return that amount, else check for the amount */
00666    sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
00667    ast_stopstream(chan);
00668    if (res == 1) {
00669       /* Stop this command, don't print a result line, as there is a new command */
00670       return RESULT_SUCCESS;
00671    }
00672 
00673    /* If the user didnt press a key, wait for digitTimeout*/
00674    if (res == 0 ) {
00675       res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
00676       /* Make sure the new result is in the escape digits of the GET OPTION */
00677       if ( !strchr(edigits,res) )
00678          res=0;
00679    }
00680 
00681         fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
00682    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00683 }
00684 
00685 
00686 
00687 
00688 /*--- handle_saynumber: Say number in various language syntaxes ---*/
00689 /* Need to add option for gender here as well. Coders wanted */
00690 /* While waiting, we're sending a (char *) NULL.  */
00691 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00692 {
00693    int res;
00694    int num;
00695    if (argc != 4)
00696       return RESULT_SHOWUSAGE;
00697    if (sscanf(argv[2], "%d", &num) != 1)
00698       return RESULT_SHOWUSAGE;
00699    res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
00700    if (res == 1)
00701       return RESULT_SUCCESS;
00702    fdprintf(agi->fd, "200 result=%d\n", res);
00703    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00704 }
00705 
00706 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00707 {
00708    int res;
00709    int num;
00710 
00711    if (argc != 4)
00712       return RESULT_SHOWUSAGE;
00713    if (sscanf(argv[2], "%d", &num) != 1)
00714       return RESULT_SHOWUSAGE;
00715 
00716    res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
00717    if (res == 1) /* New command */
00718       return RESULT_SUCCESS;
00719    fdprintf(agi->fd, "200 result=%d\n", res);
00720    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00721 }
00722 
00723 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00724 {
00725    int res;
00726 
00727    if (argc != 4)
00728       return RESULT_SHOWUSAGE;
00729 
00730    res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
00731    if (res == 1) /* New command */
00732       return RESULT_SUCCESS;
00733    fdprintf(agi->fd, "200 result=%d\n", res);
00734    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00735 }
00736 
00737 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00738 {
00739    int res;
00740    int num;
00741    if (argc != 4)
00742       return RESULT_SHOWUSAGE;
00743    if (sscanf(argv[2], "%d", &num) != 1)
00744       return RESULT_SHOWUSAGE;
00745    res = ast_say_date(chan, num, argv[3], chan->language);
00746    if (res == 1)
00747       return RESULT_SUCCESS;
00748    fdprintf(agi->fd, "200 result=%d\n", res);
00749    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00750 }
00751 
00752 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00753 {
00754    int res;
00755    int num;
00756    if (argc != 4)
00757       return RESULT_SHOWUSAGE;
00758    if (sscanf(argv[2], "%d", &num) != 1)
00759       return RESULT_SHOWUSAGE;
00760    res = ast_say_time(chan, num, argv[3], chan->language);
00761    if (res == 1)
00762       return RESULT_SUCCESS;
00763    fdprintf(agi->fd, "200 result=%d\n", res);
00764    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00765 }
00766 
00767 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00768 {
00769    int res=0;
00770    time_t unixtime;
00771    char *format, *zone=NULL;
00772    
00773    if (argc < 4)
00774       return RESULT_SHOWUSAGE;
00775 
00776    if (argc > 4) {
00777       format = argv[4];
00778    } else {
00779       /* XXX this doesn't belong here, but in the 'say' module */
00780       if (!strcasecmp(chan->language, "de")) {
00781          format = "A dBY HMS";
00782       } else {
00783          format = "ABdY 'digits/at' IMp"; 
00784       }
00785    }
00786 
00787    if (argc > 5 && !ast_strlen_zero(argv[5]))
00788       zone = argv[5];
00789 
00790    if (ast_get_time_t(argv[2], &unixtime, 0, NULL))
00791       return RESULT_SHOWUSAGE;
00792 
00793    res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone);
00794    if (res == 1)
00795       return RESULT_SUCCESS;
00796 
00797    fdprintf(agi->fd, "200 result=%d\n", res);
00798    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00799 }
00800 
00801 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00802 {
00803    int res;
00804 
00805    if (argc != 4)
00806       return RESULT_SHOWUSAGE;
00807 
00808    res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
00809    if (res == 1) /* New command */
00810       return RESULT_SUCCESS;
00811    fdprintf(agi->fd, "200 result=%d\n", res);
00812    return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00813 }
00814 
00815 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00816 {
00817    int res;
00818    char data[1024];
00819    int max;
00820    int timeout;
00821 
00822    if (argc < 3)
00823       return RESULT_SHOWUSAGE;
00824    if (argc >= 4)
00825       timeout = atoi(argv[3]); 
00826    else
00827       timeout = 0;
00828    if (argc >= 5) 
00829       max = atoi(argv[4]); 
00830    else
00831       max = 1024;
00832    res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
00833    if (res == 2)        /* New command */
00834       return RESULT_SUCCESS;
00835    else if (res == 1)
00836       fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
00837    else if (res < 0 )
00838       fdprintf(agi->fd, "200 result=-1\n");
00839    else
00840       fdprintf(agi->fd, "200 result=%s\n", data);
00841    return RESULT_SUCCESS;
00842 }
00843 
00844 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00845 {
00846 
00847    if (argc != 3)
00848       return RESULT_SHOWUSAGE;
00849    ast_copy_string(chan->context, argv[2], sizeof(chan->context));
00850    fdprintf(agi->fd, "200 result=0\n");
00851    return RESULT_SUCCESS;
00852 }
00853    
00854 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00855 {
00856    if (argc != 3)
00857       return RESULT_SHOWUSAGE;
00858    ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
00859    fdprintf(agi->fd, "200 result=0\n");
00860    return RESULT_SUCCESS;
00861 }
00862 
00863 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00864 {
00865    int pri;
00866    if (argc != 3)
00867       return RESULT_SHOWUSAGE;   
00868 
00869    if (sscanf(argv[2], "%d", &pri) != 1) {
00870       if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
00871          return RESULT_SHOWUSAGE;
00872    }
00873 
00874    ast_explicit_goto(chan, NULL, NULL, pri);
00875    fdprintf(agi->fd, "200 result=0\n");
00876    return RESULT_SUCCESS;
00877 }
00878       
00879 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00880 {
00881    struct ast_filestream *fs;
00882    struct ast_frame *f;
00883    struct timeval start;
00884    long sample_offset = 0;
00885    int res = 0;
00886    int ms;
00887 
00888         struct ast_dsp *sildet=NULL;         /* silence detector dsp */
00889         int totalsilence = 0;
00890         int dspsilence = 0;
00891         int silence = 0;                /* amount of silence to allow */
00892         int gotsilence = 0;             /* did we timeout for silence? */
00893         char *silencestr=NULL;
00894         int rfmt=0;
00895 
00896 
00897    /* XXX EAGI FIXME XXX */
00898 
00899    if (argc < 6)
00900       return RESULT_SHOWUSAGE;
00901    if (sscanf(argv[5], "%d", &ms) != 1)
00902       return RESULT_SHOWUSAGE;
00903 
00904    if (argc > 6)
00905       silencestr = strchr(argv[6],'s');
00906    if ((argc > 7) && (!silencestr))
00907       silencestr = strchr(argv[7],'s');
00908    if ((argc > 8) && (!silencestr))
00909       silencestr = strchr(argv[8],'s');
00910 
00911    if (silencestr) {
00912       if (strlen(silencestr) > 2) {
00913          if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
00914             silencestr++;
00915             silencestr++;
00916             if (silencestr)
00917                         silence = atoi(silencestr);
00918                if (silence > 0)
00919                         silence *= 1000;
00920             }
00921       }
00922    }
00923 
00924         if (silence > 0) {
00925          rfmt = chan->readformat;
00926                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00927                 if (res < 0) {
00928                   ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00929                         return -1;
00930                 }
00931                   sildet = ast_dsp_new();
00932                 if (!sildet) {
00933                   ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00934                         return -1;
00935                 }
00936                   ast_dsp_set_threshold(sildet, 256);
00937          }
00938 
00939    /* backward compatibility, if no offset given, arg[6] would have been
00940     * caught below and taken to be a beep, else if it is a digit then it is a
00941     * offset */
00942    if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
00943       res = ast_streamfile(chan, "beep", chan->language);
00944 
00945    if ((argc > 7) && (!strchr(argv[7], '=')))
00946       res = ast_streamfile(chan, "beep", chan->language);
00947 
00948    if (!res)
00949       res = ast_waitstream(chan, argv[4]);
00950    if (res) {
00951       fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
00952    } else {
00953       fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
00954       if (!fs) {
00955          res = -1;
00956          fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
00957          if (sildet)
00958             ast_dsp_free(sildet);
00959          return RESULT_FAILURE;
00960       }
00961       
00962       /* Request a video update */
00963       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00964    
00965       chan->stream = fs;
00966       ast_applystream(chan,fs);
00967       /* really should have checks */
00968       ast_seekstream(fs, sample_offset, SEEK_SET);
00969       ast_truncstream(fs);
00970       
00971       start = ast_tvnow();
00972       while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
00973          res = ast_waitfor(chan, -1);
00974          if (res < 0) {
00975             ast_closestream(fs);
00976             fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
00977             if (sildet)
00978                ast_dsp_free(sildet);
00979             return RESULT_FAILURE;
00980          }
00981          f = ast_read(chan);
00982          if (!f) {
00983             fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
00984             ast_closestream(fs);
00985             if (sildet)
00986                ast_dsp_free(sildet);
00987             return RESULT_FAILURE;
00988          }
00989          switch(f->frametype) {
00990          case AST_FRAME_DTMF:
00991             if (strchr(argv[4], f->subclass)) {
00992                /* This is an interrupting chracter, so rewind to chop off any small
00993                   amount of DTMF that may have been recorded
00994                */
0