Fri Feb 19 17:12:46 2010

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

Generated on Fri Feb 19 17:12:46 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7