Sat Aug 6 00:39:31 2011

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: 314822 $")
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                fdprintf(agi->fd, "200 result=%d\n", res);
00956                return RESULT_FAILURE;
00957                 }
00958                   sildet = ast_dsp_new();
00959                 if (!sildet) {
00960                   ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00961                fdprintf(agi->fd, "200 result=-1\n");
00962                return RESULT_FAILURE;
00963                 }
00964                   ast_dsp_set_threshold(sildet, 256);
00965          }
00966 
00967    /* backward compatibility, if no offset given, arg[6] would have been
00968     * caught below and taken to be a beep, else if it is a digit then it is a
00969     * offset */
00970    if ((argc >6) && (sscanf(argv[6], "%30ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
00971       res = ast_streamfile(chan, "beep", chan->language);
00972 
00973    if ((argc > 7) && (!strchr(argv[7], '=')))
00974       res = ast_streamfile(chan, "beep", chan->language);
00975 
00976    if (!res)
00977       res = ast_waitstream(chan, argv[4]);
00978    if (res) {
00979       fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
00980    } else {
00981       fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
00982       if (!fs) {
00983          res = -1;
00984          fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
00985          if (sildet)
00986             ast_dsp_free(sildet);
00987          return RESULT_FAILURE;
00988       }
00989       
00990       /* Request a video update */
00991       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00992    
00993       chan->stream = fs;
00994       ast_applystream(chan,fs);
00995       /* really should have checks */
00996       ast_seekstream(fs, sample_offset, SEEK_SET);
00997       ast_truncstream(fs);
00998       
00999       start = ast_tvnow();
01000       while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
01001          res = ast_waitfor(chan, ms - ast_tvdiff_ms(ast_tvnow(), start));
01002          if (res < 0) {
01003             ast_closestream(fs);
01004             fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
01005             if (sildet)
01006                ast_dsp_free(sildet);
01007             return RESULT_FAILURE;
01008          }
01009          f = ast_read(chan);
01010          if (!f) {
01011             fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
01012             ast_closestream(fs);
01013             if (sildet)
01014                ast_dsp_free(sildet);
01015             return RESULT_FAILURE;
01016          }
01017          switch(f->frametype) {
01018          case AST_FRAME_DTMF:
01019             if (strchr(argv[4], f->subclass)) {
01020                /* This is an interrupting chracter, so rewind to chop off any small
01021                   amount of DTMF that may have been recorded
01022                */
01023                ast_stream_rewind(fs, 200);
01024                ast_truncstream(fs);
01025                sample_offset = ast_tellstream(fs);
01026                fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
01027                ast_closestream(fs);
01028                ast_frfree(f);
01029                if (sildet)
01030                   ast_dsp_free(sildet);
01031                return RESULT_SUCCESS;
01032             }
01033             break;
01034          case AST_FRAME_VOICE:
01035             ast_writestream(fs, f);
01036             /* this is a safe place to check progress since we know that fs
01037              * is valid after a write, and it will then have our current
01038              * location */
01039             sample_offset = ast_tellstream(fs);
01040                                 if (silence > 0) {
01041                                  dspsilence = 0;
01042                                         ast_dsp_silence(sildet, f, &dspsilence);
01043                                         if (dspsilence) {
01044                                              totalsilence = dspsilence;
01045                                         } else {
01046                                                 totalsilence = 0;
01047                                         }
01048                                         if (totalsilence > silence) {
01049                                              /* Ended happily with silence */
01050                                                 gotsilence = 1;
01051                                                 break;
01052                                         }
01053                               }
01054             break;
01055          case AST_FRAME_VIDEO:
01056             ast_writestream(fs, f);
01057          default:
01058             /* Ignore all other frames */
01059             break;
01060          }
01061          ast_frfree(f);
01062          if (gotsilence)
01063             break;
01064          }
01065 
01066                if (gotsilence) {
01067                         ast_stream_rewind(fs, silence-1000);
01068                   ast_truncstream(fs);
01069          sample_offset = ast_tellstream(fs);
01070       }     
01071       fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
01072       ast_closestream(fs);
01073    }
01074 
01075         if (silence > 0) {
01076                 res = ast_set_read_format(chan, rfmt);
01077                 if (res)
01078                         ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
01079                 ast_dsp_free(sildet);
01080         }
01081    return RESULT_SUCCESS;
01082 }
01083 
01084 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01085 {
01086    int timeout;
01087 
01088    if (argc != 3)
01089       return RESULT_SHOWUSAGE;
01090    if (sscanf(argv[2], "%30d", &timeout) != 1)
01091       return RESULT_SHOWUSAGE;
01092    if (timeout < 0)
01093       timeout = 0;
01094    if (timeout)
01095       chan->whentohangup = time(NULL) + timeout;
01096    else
01097       chan->whentohangup = 0;
01098    fdprintf(agi->fd, "200 result=0\n");
01099    return RESULT_SUCCESS;
01100 }
01101 
01102 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01103 {
01104    struct ast_channel *c;
01105    if (argc == 1) {
01106       /* no argument: hangup the current channel */
01107       ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
01108       fdprintf(agi->fd, "200 result=1\n");
01109       return RESULT_SUCCESS;
01110    } else if (argc == 2) {
01111       /* one argument: look for info on the specified channel */
01112       c = ast_get_channel_by_name_locked(argv[1]);
01113       if (c) {
01114          /* we have a matching channel */
01115          ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
01116          fdprintf(agi->fd, "200 result=1\n");
01117          ast_channel_unlock(c);
01118          return RESULT_SUCCESS;
01119       }
01120       /* if we get this far no channel name matched the argument given */
01121       fdprintf(agi->fd, "200 result=-1\n");
01122       return RESULT_SUCCESS;
01123    } else {
01124       return RESULT_SHOWUSAGE;
01125    }
01126 }
01127 
01128 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01129 {
01130    int res, workaround;
01131    struct ast_app *app;
01132 
01133    if (argc < 2)
01134       return RESULT_SHOWUSAGE;
01135 
01136    if (option_verbose > 2)
01137       ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
01138 
01139    app = pbx_findapp(argv[1]);
01140 
01141    if (app) {
01142       if(!strcasecmp(argv[1], PARK_APP_NAME)) {
01143          ast_masq_park_call(chan, NULL, 0, NULL);
01144       }
01145       if (!(workaround = ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS))) {
01146          ast_set_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
01147       }
01148       res = pbx_exec(chan, app, argc == 2 ? "" : argv[2]);
01149       if (!workaround) {
01150          ast_clear_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
01151       }
01152    } else {
01153       ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01154       res = -2;
01155    }
01156    fdprintf(agi->fd, "200 result=%d\n", res);
01157 
01158    /* Even though this is wrong, users are depending upon this result. */
01159    return res;
01160 }
01161 
01162 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01163 {
01164    char tmp[256]="";
01165    char *l = NULL, *n = NULL;
01166 
01167    if (argv[2]) {
01168       ast_copy_string(tmp, argv[2], sizeof(tmp));
01169       ast_callerid_parse(tmp, &n, &l);
01170       if (l)
01171          ast_shrink_phone_number(l);
01172       else
01173          l = "";
01174       if (!n)
01175          n = "";
01176       ast_set_callerid(chan, l, n, NULL);
01177    }
01178 
01179    fdprintf(agi->fd, "200 result=1\n");
01180    return RESULT_SUCCESS;
01181 }
01182 
01183 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01184 {
01185    struct ast_channel *c;
01186    if (argc == 2) {
01187       /* no argument: supply info on the current channel */
01188       fdprintf(agi->fd, "200 result=%d\n", chan->_state);
01189       return RESULT_SUCCESS;
01190    } else if (argc == 3) {
01191       /* one argument: look for info on the specified channel */
01192       c = ast_get_channel_by_name_locked(argv[2]);
01193       if (c) {
01194          fdprintf(agi->fd, "200 result=%d\n", c->_state);
01195          ast_channel_unlock(c);
01196          return RESULT_SUCCESS;
01197       }
01198       /* if we get this far no channel name matched the argument given */
01199       fdprintf(agi->fd, "200 result=-1\n");
01200       return RESULT_SUCCESS;
01201    } else {
01202       return RESULT_SHOWUSAGE;
01203    }
01204 }
01205 
01206 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01207 {
01208    if (argv[3])
01209       pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01210 
01211    fdprintf(agi->fd, "200 result=1\n");
01212    return RESULT_SUCCESS;
01213 }
01214 
01215 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01216 {
01217    char *ret;
01218    char tempstr[1024] = "";
01219 
01220    if (argc != 3)
01221       return RESULT_SHOWUSAGE;
01222 
01223    /* check if we want to execute an ast_custom_function */
01224    if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01225       ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01226    } else {
01227       pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01228    }
01229 
01230    if (ret)
01231       fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
01232    else
01233       fdprintf(agi->fd, "200 result=0\n");
01234 
01235    return RESULT_SUCCESS;
01236 }
01237 
01238 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01239 {
01240    char tmp[4096] = "";
01241    struct ast_channel *chan2=NULL;
01242 
01243    if ((argc != 4) && (argc != 5))
01244       return RESULT_SHOWUSAGE;
01245    if (argc == 5 && strcasecmp(chan->name, argv[4])) {
01246       chan2 = ast_get_channel_by_name_locked(argv[4]);
01247    } else {
01248       chan2 = chan;
01249    }
01250    if (chan2) {
01251       pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01252       fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
01253    } else {
01254       fdprintf(agi->fd, "200 result=0\n");
01255    }
01256    if (chan2 && (chan2 != chan))
01257       ast_channel_unlock(chan2);
01258    return RESULT_SUCCESS;
01259 }
01260 
01261 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01262 {
01263    int level = 0;
01264    char *prefix;
01265 
01266    if (argc < 2)
01267       return RESULT_SHOWUSAGE;
01268 
01269    if (argv[2])
01270       sscanf(argv[2], "%30d", &level);
01271 
01272    switch (level) {
01273       case 4:
01274          prefix = VERBOSE_PREFIX_4;
01275          break;
01276       case 3:
01277          prefix = VERBOSE_PREFIX_3;
01278          break;
01279       case 2:
01280          prefix = VERBOSE_PREFIX_2;
01281          break;
01282       case 1:
01283       default:
01284          prefix = VERBOSE_PREFIX_1;
01285          break;
01286    }
01287 
01288    if (level <= option_verbose)
01289       ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
01290    
01291    fdprintf(agi->fd, "200 result=1\n");
01292    
01293    return RESULT_SUCCESS;
01294 }
01295 
01296 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01297 {
01298    int res;
01299    size_t bufsize = 16;
01300    char *buf, *tmp;
01301 
01302    if (argc != 4)
01303       return RESULT_SHOWUSAGE;
01304 
01305    if (!(buf = ast_malloc(bufsize))) {
01306       fdprintf(agi->fd, "200 result=-1\n");
01307       return RESULT_SUCCESS;
01308    }
01309 
01310    do {
01311       res = ast_db_get(argv[2], argv[3], buf, bufsize);
01312       if (strlen(buf) < bufsize - 1) {
01313          break;
01314       }
01315       bufsize *= 2;
01316       if (!(tmp = ast_realloc(buf, bufsize))) {
01317          break;
01318       }
01319       buf = tmp;
01320    } while (1);
01321    
01322    if (res) 
01323       fdprintf(agi->fd, "200 result=0\n");
01324    else
01325       fdprintf(agi->fd, "200 result=1 (%s)\n", buf);
01326 
01327    ast_free(buf);
01328    return RESULT_SUCCESS;
01329 }
01330 
01331 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01332 {
01333    int res;
01334 
01335    if (argc != 5)
01336       return RESULT_SHOWUSAGE;
01337    res = ast_db_put(argv[2], argv[3], argv[4]);
01338    fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01339    return RESULT_SUCCESS;
01340 }
01341 
01342 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01343 {
01344    int res;
01345 
01346    if (argc != 4)
01347       return RESULT_SHOWUSAGE;
01348    res = ast_db_del(argv[2], argv[3]);
01349    fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01350    return RESULT_SUCCESS;
01351 }
01352 
01353 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01354 {
01355    int res;
01356    if ((argc < 3) || (argc > 4))
01357       return RESULT_SHOWUSAGE;
01358    if (argc == 4)
01359       res = ast_db_deltree(argv[2], argv[3]);
01360    else
01361       res = ast_db_deltree(argv[2], NULL);
01362 
01363    fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01364    return RESULT_SUCCESS;
01365 }
01366 
01367 static char debug_usage[] = 
01368 "Usage: agi debug\n"
01369 "       Enables dumping of AGI transactions for debugging purposes\n";
01370 
01371 static char no_debug_usage[] = 
01372 "Usage: agi debug off\n"
01373 "       Disables dumping of AGI transactions for debugging purposes\n";
01374 
01375 static int agi_do_debug(int fd, int argc, char *argv[])
01376 {
01377    if (argc != 2)
01378       return RESULT_SHOWUSAGE;
01379    agidebug = 1;
01380    ast_cli(fd, "AGI Debugging Enabled\n");
01381    return RESULT_SUCCESS;
01382 }
01383 
01384 static int agi_no_debug_deprecated(int fd, int argc, char *argv[])
01385 {
01386    if (argc != 3)
01387       return RESULT_SHOWUSAGE;
01388    agidebug = 0;
01389    ast_cli(fd, "AGI Debugging Disabled\n");
01390    return RESULT_SUCCESS;
01391 }
01392 
01393 static int agi_no_debug(int fd, int argc, char *argv[])
01394 {
01395    if (argc != 3)
01396       return RESULT_SHOWUSAGE;
01397    agidebug = 0;
01398    ast_cli(fd, "AGI Debugging Disabled\n");
01399    return RESULT_SUCCESS;
01400 }
01401 
01402 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01403 {
01404    fdprintf(agi->fd, "200 result=0\n");
01405    return RESULT_SUCCESS;
01406 }
01407 
01408 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01409 {
01410    if (argc < 3) {
01411       return RESULT_SHOWUSAGE;
01412    }
01413    if (!strncasecmp(argv[2], "on", 2))
01414       ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01415    else if (!strncasecmp(argv[2], "off", 3))
01416       ast_moh_stop(chan);
01417    fdprintf(agi->fd, "200 result=0\n");
01418    return RESULT_SUCCESS;
01419 }
01420 
01421 static char usage_setmusic[] =
01422 " Usage: SET MUSIC ON <on|off> <class>\n"
01423 "  Enables/Disables the music on hold generator.  If <class> is\n"
01424 " not specified, then the default music on hold class will be used.\n"
01425 " Always returns 0.\n";
01426 
01427 static char usage_dbput[] =
01428 " Usage: DATABASE PUT <family> <key> <value>\n"
01429 "  Adds or updates an entry in the Asterisk database for a\n"
01430 " given family, key, and value.\n"
01431 " Returns 1 if successful, 0 otherwise.\n";
01432 
01433 static char usage_dbget[] =
01434 " Usage: DATABASE GET <family> <key>\n"
01435 "  Retrieves an entry in the Asterisk database for a\n"
01436 " given family and key.\n"
01437 " Returns 0 if <key> is not set.  Returns 1 if <key>\n"
01438 " is set and returns the variable in parentheses.\n"
01439 " Example return code: 200 result=1 (testvariable)\n";
01440 
01441 static char usage_dbdel[] =
01442 " Usage: DATABASE DEL <family> <key>\n"
01443 "  Deletes an entry in the Asterisk database for a\n"
01444 " given family and key.\n"
01445 " Returns 1 if successful, 0 otherwise.\n";
01446 
01447 static char usage_dbdeltree[] =
01448 " Usage: DATABASE DELTREE <family> [keytree]\n"
01449 "  Deletes a family or specific keytree within a family\n"
01450 " in the Asterisk database.\n"
01451 " Returns 1 if successful, 0 otherwise.\n";
01452 
01453 static char usage_verbose[] =
01454 " Usage: VERBOSE <message> <level>\n"
01455 "  Sends <message> to the console via verbose message system.\n"
01456 " <level> is the the verbose level (1-4)\n"
01457 " Always returns 1.\n";
01458 
01459 static char usage_getvariable[] =
01460 " Usage: GET VARIABLE <variablename>\n"
01461 "  Returns 0 if <variablename> is not set.  Returns 1 if <variablename>\n"
01462 " is set and returns the variable in parentheses.\n"
01463 " example return code: 200 result=1 (testvariable)\n";
01464 
01465 static char usage_getvariablefull[] =
01466 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
01467 "  Returns 0 if <variablename> is not set or channel does not exist.  Returns 1\n"
01468 "if <variablename>  is set and returns the variable in parenthesis.  Understands\n"
01469 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
01470 " example return code: 200 result=1 (testvariable)\n";
01471 
01472 static char usage_setvariable[] =
01473 " Usage: SET VARIABLE <variablename> <value>\n";
01474 
01475 static char usage_channelstatus[] =
01476 " Usage: CHANNEL STATUS [<channelname>]\n"
01477 "  Returns the status of the specified channel.\n" 
01478 " If no channel name is given the returns the status of the\n"
01479 " current channel.  Return values:\n"
01480 "  0 Channel is down and available\n"
01481 "  1 Channel is down, but reserved\n"
01482 "  2 Channel is off hook\n"
01483 "  3 Digits (or equivalent) have been dialed\n"
01484 "  4 Line is ringing\n"
01485 "  5 Remote end is ringing\n"
01486 "  6 Line is up\n"
01487 "  7 Line is busy\n";
01488 
01489 static char usage_setcallerid[] =
01490 " Usage: SET CALLERID <number>\n"
01491 "  Changes the callerid of the current channel.\n";
01492 
01493 static char usage_exec[] =
01494 " Usage: EXEC <application> <options>\n"
01495 "  Executes <application> with given <options>.\n"
01496 " Returns whatever the application returns, or -2 on failure to find application\n";
01497 
01498 static char usage_hangup[] =
01499 " Usage: HANGUP [<channelname>]\n"
01500 "  Hangs up the specified channel.\n"
01501 " If no channel name is given, hangs up the current channel\n";
01502 
01503 static char usage_answer[] = 
01504 " Usage: ANSWER\n"
01505 "  Answers channel if not already in answer state. Returns -1 on\n"
01506 " channel failure, or 0 if successful.\n";
01507 
01508 static char usage_waitfordigit[] = 
01509 " Usage: WAIT FOR DIGIT <timeout>\n"
01510 "  Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
01511 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
01512 " the numerical value of the ascii of the digit if one is received.  Use -1\n"
01513 " for the timeout value if you desire the call to block indefinitely.\n";
01514 
01515 static char usage_sendtext[] =
01516 " Usage: SEND TEXT \"<text to send>\"\n"
01517 "  Sends the given text on a channel. Most channels do not support the\n"
01518 " transmission of text.  Returns 0 if text is sent, or if the channel does not\n"
01519 " support text transmission.  Returns -1 only on error/hangup.  Text\n"
01520 " consisting of greater than one word should be placed in quotes since the\n"
01521 " command only accepts a single argument.\n";
01522 
01523 static char usage_recvchar[] =
01524 " Usage: RECEIVE CHAR <timeout>\n"
01525 "  Receives a character of text on a channel. Specify timeout to be the\n"
01526 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
01527 " do not support the reception of text. Returns the decimal value of the character\n"
01528 " if one is received, or 0 if the channel does not support text reception.  Returns\n"
01529 " -1 only on error/hangup.\n";
01530 
01531 static char usage_recvtext[] =
01532 " Usage: RECEIVE TEXT <timeout>\n"
01533 "  Receives a string of text on a channel. Specify timeout to be the\n"
01534 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
01535 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
01536 
01537 static char usage_tddmode[] =
01538 " Usage: TDD MODE <on|off>\n"
01539 "  Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
01540 " successful, or 0 if channel is not TDD-capable.\n";
01541 
01542 static char usage_sendimage[] =
01543 " Usage: SEND IMAGE <image>\n"
01544 "  Sends the given image on a channel. Most channels do not support the\n"
01545 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
01546 " support image transmission.  Returns -1 only on error/hangup. Image names\n"
01547 " should not include extensions.\n";
01548 
01549 static char usage_streamfile[] =
01550 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
01551 "  Send the given file, allowing playback to be interrupted by the given\n"
01552 " digits, if any. Use double quotes for the digits if you wish none to be\n"
01553 " permitted. If sample offset is provided then the audio will seek to sample\n"
01554 " offset before play starts.  Returns 0 if playback completes without a digit\n"
01555 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
01556 " or -1 on error or if the channel was disconnected. Remember, the file\n"
01557 " extension must not be included in the filename.\n";
01558 
01559 static char usage_controlstreamfile[] =
01560 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
01561 "  Send the given file, allowing playback to be controled by the given\n"
01562 " digits, if any. Use double quotes for the digits if you wish none to be\n"
01563 " permitted.  Returns 0 if playback completes without a digit\n"
01564 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
01565 " or -1 on error or if the channel was disconnected. Remember, the file\n"
01566 " extension must not be included in the filename.\n\n"
01567 " Note: ffchar and rewchar default to * and # respectively.\n";
01568 
01569 static char usage_getoption[] = 
01570 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
01571 "  Behaves similar to STREAM FILE but used with a timeout option.\n";
01572 
01573 static char usage_saynumber[] =
01574 " Usage: SAY NUMBER <number> <escape digits>\n"
01575 "  Say a given number, returning early if any of the given DTMF digits\n"
01576 " are received on the channel.  Returns 0 if playback completes without a digit\n"
01577 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01578 " -1 on error/hangup.\n";
01579 
01580 static char usage_saydigits[] =
01581 " Usage: SAY DIGITS <number> <escape digits>\n"
01582 "  Say a given digit string, returning early if any of the given DTMF digits\n"
01583 " are received on the channel. Returns 0 if playback completes without a digit\n"
01584 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01585 " -1 on error/hangup.\n";
01586 
01587 static char usage_sayalpha[] =
01588 " Usage: SAY ALPHA <number> <escape digits>\n"
01589 "  Say a given character string, returning early if any of the given DTMF digits\n"
01590 " are received on the channel. Returns 0 if playback completes without a digit\n"
01591 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01592 " -1 on error/hangup.\n";
01593 
01594 static char usage_saydate[] =
01595 " Usage: SAY DATE <date> <escape digits>\n"
01596 "  Say a given date, returning early if any of the given DTMF digits are\n"
01597 " received on the channel.  <date> is number of seconds elapsed since 00:00:00\n"
01598 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
01599 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01600 " digit if one was pressed or -1 on error/hangup.\n";
01601 
01602 static char usage_saytime[] =
01603 " Usage: SAY TIME <time> <escape digits>\n"
01604 "  Say a given time, returning early if any of the given DTMF digits are\n"
01605 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
01606 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
01607 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01608 " digit if one was pressed or -1 on error/hangup.\n";
01609 
01610 static char usage_saydatetime[] =
01611 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
01612 "  Say a given time, returning early if any of the given DTMF digits are\n"
01613 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
01614 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
01615 " the time should be said in.  See voicemail.conf (defaults to \"ABdY\n"
01616 " 'digits/at' IMp\").  Acceptable values for [timezone] can be found in\n"
01617 " /usr/share/zoneinfo.  Defaults to machine default. Returns 0 if playback\n"
01618 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01619 " digit if one was pressed or -1 on error/hangup.\n";
01620 
01621 static char usage_sayphonetic[] =
01622 " Usage: SAY PHONETIC <string> <escape digits>\n"
01623 "  Say a given character string with phonetics, returning early if any of the\n"
01624 " given DTMF digits are received on the channel. Returns 0 if playback\n"
01625 " completes without a digit pressed, the ASCII numerical value of the digit\n"
01626 " if one was pressed, or -1 on error/hangup.\n";
01627 
01628 static char usage_getdata[] =
01629 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
01630 "  Stream the given file, and recieve DTMF data. Returns the digits received\n"
01631 "from the channel at the other end.\n";
01632 
01633 static char usage_setcontext[] =
01634 " Usage: SET CONTEXT <desired context>\n"
01635 "  Sets the context for continuation upon exiting the application.\n";
01636 
01637 static char usage_setextension[] =
01638 " Usage: SET EXTENSION <new extension>\n"
01639 "  Changes the extension for continuation upon exiting the application.\n";
01640 
01641 static char usage_setpriority[] =
01642 " Usage: SET PRIORITY <priority>\n"
01643 "  Changes the priority for continuation upon exiting the application.\n"
01644 " The priority must be a valid priority or label.\n";
01645 
01646 static char usage_recordfile[] =
01647 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
01648 "                                          [offset samples] [BEEP] [s=silence]\n"
01649 "  Record to a file until a given dtmf digit in the sequence is received\n"
01650 " Returns -1 on hangup or error.  The format will specify what kind of file\n"
01651 " will be recorded.  The timeout is the maximum record time in milliseconds, or\n"
01652 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
01653 " to the offset without exceeding the end of the file.  \"silence\" is the number\n"
01654 " of seconds of silence allowed before the function returns despite the\n"
01655 " lack of dtmf digits or reaching timeout.  Silence value must be\n"
01656 " preceded by \"s=\" and is also optional.\n";
01657 
01658 static char usage_autohangup[] =
01659 " Usage: SET AUTOHANGUP <time>\n"
01660 "  Cause the channel to automatically hangup at <time> seconds in the\n"
01661 " future.  Of course it can be hungup before then as well. Setting to 0 will\n"
01662 " cause the autohangup feature to be disabled on this channel.\n";
01663 
01664 static char usage_noop[] =
01665 " Usage: NoOp\n"
01666 "  Does nothing.\n";
01667 
01668 static agi_command commands[MAX_COMMANDS] = {
01669    { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
01670    { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
01671    { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
01672    { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
01673    { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
01674    { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
01675    { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
01676    { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
01677    { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
01678    { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
01679    { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
01680    { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
01681    { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
01682    { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar },
01683    { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext },
01684    { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
01685    { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
01686    { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
01687    { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
01688    { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
01689    { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate },
01690    { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
01691    { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime },
01692    { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
01693    { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
01694    { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
01695    { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
01696    { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
01697    { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
01698    { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
01699    { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
01700    { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
01701    { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
01702    { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile },
01703    { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
01704    { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
01705    { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
01706 };
01707 
01708 static int help_workhorse(int fd, char *match[])
01709 {
01710    char fullcmd[80];
01711    char matchstr[80];
01712    int x;
01713    struct agi_command *e;
01714    if (match)
01715       ast_join(matchstr, sizeof(matchstr), match);
01716    for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
01717       e = &commands[x]; 
01718       if (!e->cmda[0])
01719          break;
01720       /* Hide commands that start with '_' */
01721       if ((e->cmda[0])[0] == '_')
01722          continue;
01723       ast_join(fullcmd, sizeof(fullcmd), e->cmda);
01724       if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
01725          continue;
01726       ast_cli(fd, "%20.20s   %s\n", fullcmd, e->summary);
01727    }
01728    return 0;
01729 }
01730 
01731 int ast_agi_register(agi_command *agi)
01732 {
01733    int x;
01734    for (x=0; x<MAX_COMMANDS - 1; x++) {
01735       if (commands[x].cmda[0] == agi->cmda[0]) {
01736          ast_log(LOG_WARNING, "Command already registered!\n");
01737          return -1;
01738       }
01739    }
01740    for (x=0; x<MAX_COMMANDS - 1; x++) {
01741       if (!commands[x].cmda[0]) {
01742          commands[x] = *agi;
01743          return 0;
01744       }
01745    }
01746    ast_log(LOG_WARNING, "No more room for new commands!\n");
01747    return -1;
01748 }
01749 
01750 void ast_agi_unregister(agi_command *agi)
01751 {
01752    int x;
01753    for (x=0; x<MAX_COMMANDS - 1; x++) {
01754       if (commands[x].cmda[0] == agi->cmda[0]) {
01755          memset(&commands[x], 0, sizeof(agi_command));
01756       }
01757    }
01758 }
01759 
01760 static agi_command *find_command(char *cmds[], int exact)
01761 {
01762    int x;
01763    int y;
01764    int match;
01765 
01766    for (x=0; x < sizeof(commands) / sizeof(commands[0]); x++) {
01767       if (!commands[x].cmda[0])
01768          break;
01769       /* start optimistic */
01770       match = 1;
01771       for (y=0; match && cmds[y]; y++) {
01772          /* If there are no more words in the command (and we're looking for
01773             an exact match) or there is a difference between the two words,
01774             then this is not a match */
01775          if (!commands[x].cmda[y] && !exact)
01776             break;
01777          /* don't segfault if the next part of a command doesn't exist */
01778          if (!commands[x].cmda[y])
01779             return NULL;
01780          if (strcasecmp(commands[x].cmda[y], cmds[y]))
01781             match = 0;
01782       }
01783       /* If more words are needed to complete the command then this is not
01784          a candidate (unless we're looking for a really inexact answer  */
01785       if ((exact > -1) && commands[x].cmda[y])
01786          match = 0;
01787       if (match)
01788          return &commands[x];
01789    }
01790    return NULL;
01791 }
01792 
01793 
01794 static int parse_args(char *s, int *max, char *argv[])
01795 {
01796    int x=0;
01797    int quoted=0;
01798    int escaped=0;
01799    int whitespace=1;
01800    char *cur;
01801 
01802    cur = s;
01803    while(*s) {
01804       switch(*s) {
01805       case '"':
01806          /* If it's escaped, put a literal quote */
01807          if (escaped) 
01808             goto normal;
01809          else 
01810             quoted = !quoted;
01811          if (quoted && whitespace) {
01812             /* If we're starting a quote, coming off white space start a new word, too */
01813             argv[x++] = cur;
01814             whitespace=0;
01815          }
01816          escaped = 0;
01817       break;
01818       case ' ':
01819       case '\t':
01820          if (!quoted && !escaped) {
01821             /* If we're not quoted, mark this as whitespace, and
01822                end the previous argument */
01823             whitespace = 1;
01824             *(cur++) = '\0';
01825          } else
01826             /* Otherwise, just treat it as anything else */ 
01827             goto normal;
01828          break;
01829       case '\\':
01830          /* If we're escaped, print a literal, otherwise enable escaping */
01831          if (escaped) {
01832             goto normal;
01833          } else {
01834             escaped=1;
01835          }
01836          break;
01837       default:
01838 normal:
01839          if (whitespace) {
01840             if (x >= MAX_ARGS -1) {
01841                ast_log(LOG_WARNING, "Too many arguments, truncating\n");
01842                break;
01843             }
01844             /* Coming off of whitespace, start the next argument */
01845             argv[x++] = cur;
01846             whitespace=0;
01847          }
01848          *(cur++) = *s;
01849          escaped=0;
01850       }
01851       s++;
01852    }
01853    /* Null terminate */
01854    *(cur++) = '\0';
01855    argv[x] = NULL;
01856    *max = x;
01857    return 0;
01858 }
01859 
01860 static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
01861 {
01862    char *argv[MAX_ARGS];
01863    int argc = MAX_ARGS;
01864    int res;
01865    agi_command *c;
01866 
01867    parse_args(buf, &argc, argv);
01868    c = find_command(argv, 0);
01869    if (c) {
01870       /* If the AGI command being executed is an actual application (using agi exec)
01871       the app field will be updated in pbx_exec via handle_exec */
01872       if (chan->cdr && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
01873          ast_cdr_setapp(chan->cdr, "AGI", buf);
01874 
01875       res = c->handler(chan, agi, argc, argv);
01876       switch(res) {
01877       case RESULT_SHOWUSAGE:
01878          fdprintf(agi->fd, "520-Invalid command syntax.  Proper usage follows:\n");
01879          fdprintf(agi->fd, "%s", c->usage);
01880          fdprintf(agi->fd, "520 End of proper usage.\n");
01881          break;
01882       case AST_PBX_KEEPALIVE:
01883          /* We've been asked to keep alive, so do so */
01884          return AST_PBX_KEEPALIVE;
01885          break;
01886       case RESULT_FAILURE:
01887          /* The RESULT_FAILURE code is usually because the channel hungup. */
01888          return AGI_RESULT_FAILURE;
01889       default:
01890          break;
01891       }
01892    } else {
01893       fdprintf(agi->fd, "510 Invalid or unknown command\n");
01894    }
01895    return AGI_RESULT_SUCCESS;
01896 }
01897 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead)
01898 {
01899    struct ast_channel *c;
01900    int outfd;
01901    int ms;
01902    enum agi_result returnstatus = AGI_RESULT_SUCCESS;
01903    struct ast_frame *f;
01904    char buf[AGI_BUF_LEN];
01905    char *res = NULL;
01906    FILE *readf;
01907    /* how many times we'll retry if ast_waitfor_nandfs will return without either 
01908      channel or file descriptor in case select is interrupted by a system call (EINTR) */
01909    int retry = AGI_NANDFS_RETRY;
01910 
01911    if (!(readf = fdopen(agi->ctrl, "r"))) {
01912       ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
01913       if (pid > -1)
01914          kill(pid, SIGHUP);
01915       close(agi->ctrl);
01916       return AGI_RESULT_FAILURE;
01917    }
01918    setlinebuf(readf);
01919    setup_env(chan, request, agi->fd, (agi->audio > -1));
01920    for (;;) {
01921       ms = -1;
01922       if (dead) {
01923          c = ast_waitfor_nandfds(&chan, 0, &agi->ctrl, 1, NULL, &outfd, &ms);
01924       } else if (!ast_check_hangup(chan)) {
01925          c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms);
01926       } else {
01927          /*
01928           * Read the channel control queue until it is dry so we can
01929           * quit.
01930           */
01931          c = chan;
01932       }
01933       if (c) {
01934          retry = AGI_NANDFS_RETRY;
01935          /* Idle the channel until we get a command */
01936          f = ast_read(c);
01937          if (!f) {
01938             ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
01939             returnstatus = AGI_RESULT_HANGUP;
01940             break;
01941          } else {
01942             /* If it's voice, write it to the audio pipe */
01943             if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
01944                /* Write, ignoring errors */
01945                if (write(agi->audio, f->data, f->datalen) < 0) {
01946                }
01947             }
01948             ast_frfree(f);
01949          }
01950       } else if (outfd > -1) {
01951          size_t len = sizeof(buf);
01952          size_t buflen = 0;
01953          enum agi_result cmd_status;
01954 
01955          retry = AGI_NANDFS_RETRY;
01956          buf[0] = '\0';
01957 
01958          while (buflen < (len - 1)) {
01959             res = fgets(buf + buflen, len, readf);
01960             if (feof(readf)) 
01961                break;
01962             if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN))) 
01963                break;
01964             if (res != NULL && !agi->fast)
01965                break;
01966             buflen = strlen(buf);
01967             if (buflen && buf[buflen - 1] == '\n')
01968                break;
01969             len -= buflen;
01970             if (agidebug)
01971                ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
01972          }
01973 
01974          if (!buf[0]) {
01975             /* Program terminated */
01976             if (option_verbose > 2) 
01977                ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
01978             if (pid > 0)
01979                waitpid(pid, status, 0);
01980             /* No need to kill the pid anymore, since they closed us */
01981             pid = -1;
01982             break;
01983          }
01984 
01985          /* Special case for inability to execute child process */
01986          if (*buf && strncasecmp(buf, "failure", 7) == 0) {
01987             returnstatus = AGI_RESULT_FAILURE;
01988             break;
01989          }
01990 
01991          /* get rid of trailing newline, if any */
01992          if (*buf && buf[strlen(buf) - 1] == '\n')
01993             buf[strlen(buf) - 1] = 0;
01994          if (agidebug)
01995             ast_verbose("AGI Rx << %s\n", buf);
01996          cmd_status = agi_handle_command(chan, agi, buf);
01997          if (cmd_status == AGI_RESULT_FAILURE) {
01998             if (dead || !ast_check_hangup(chan)) {
01999                /* The failure was not because of a hangup. */
02000                returnstatus = AGI_RESULT_FAILURE;
02001                break;
02002             }
02003          } else if (cmd_status == AST_PBX_KEEPALIVE) {
02004             returnstatus = AST_PBX_KEEPALIVE;
02005             break;
02006          }
02007       } else {
02008          if (--retry <= 0) {
02009             ast_log(LOG_WARNING, "No channel, no fd?\n");
02010             returnstatus = AGI_RESULT_FAILURE;
02011             break;
02012          }
02013       }
02014    }
02015    /* Notify process */
02016    if (pid > -1) {
02017       const char *sighup = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
02018       if (ast_strlen_zero(sighup) || !ast_false(sighup)) {
02019          if (kill(pid, SIGHUP)) {
02020             ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
02021          } else { /* Give the process a chance to die */
02022             usleep(1);
02023          }
02024       }
02025       /* This is essentially doing the same as without WNOHANG, except that
02026        * it allows the main thread to proceed, even without the child PID
02027        * dying immediately (the child may be doing cleanup, etc.).  Without
02028        * this code, zombie processes accumulate for as long as child
02029        * processes exist (which on busy systems may be always, filling up the
02030        * process table).
02031        *
02032        * Note that in trunk, we don't stop interaction at the hangup event
02033        * (instead we transparently switch to DeadAGI operation), so this is a
02034        * short-lived code addition.
02035        */
02036       if (waitpid(pid, status, WNOHANG) == 0) {
02037          struct zombie *cur = ast_calloc(1, sizeof(*cur));
02038          if (cur) {
02039             cur->pid = pid;
02040             AST_LIST_LOCK(&zombies);
02041             AST_LIST_INSERT_TAIL(&zombies, cur, list);
02042             AST_LIST_UNLOCK(&zombies);
02043          }
02044       }
02045    }
02046    fclose(readf);
02047    return returnstatus;
02048 }
02049 
02050 static int handle_showagi(int fd, int argc, char *argv[])
02051 {
02052    struct agi_command *e;
02053    char fullcmd[80];
02054    if ((argc < 2))
02055       return RESULT_SHOWUSAGE;
02056    if (argc > 2) {
02057       e = find_command(argv + 2, 1);
02058       if (e) 
02059          ast_cli(fd, "%s", e->usage);
02060       else {
02061          if (find_command(argv + 2, -1)) {
02062             return help_workhorse(fd, argv + 1);
02063          } else {
02064             ast_join(fullcmd, sizeof(fullcmd), argv+1);
02065             ast_cli(fd, "No such command '%s'.\n", fullcmd);
02066          }
02067       }
02068    } else {
02069       return help_workhorse(fd, NULL);
02070    }
02071    return RESULT_SUCCESS;
02072 }
02073 
02074 static int handle_agidumphtml(int fd, int argc, char *argv[])
02075 {
02076    struct agi_command *e;
02077    char fullcmd[80];
02078    int x;
02079    FILE *htmlfile;
02080 
02081    if ((argc < 3))
02082       return RESULT_SHOWUSAGE;
02083 
02084    if (!(htmlfile = fopen(argv[2], "wt"))) {
02085       ast_cli(fd, "Could not create file '%s'\n", argv[2]);
02086       return RESULT_SHOWUSAGE;
02087    }
02088 
02089    fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
02090    fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
02091 
02092 
02093    fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
02094 
02095    for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
02096       char *stringp, *tempstr;
02097 
02098       e = &commands[x]; 
02099       if (!e->cmda[0])  /* end ? */
02100          break;
02101       /* Hide commands that start with '_' */
02102       if ((e->cmda[0])[0] == '_')
02103          continue;
02104       ast_join(fullcmd, sizeof(fullcmd), e->cmda);
02105 
02106       fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
02107       fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd,e->summary);
02108 
02109       stringp=e->usage;
02110       tempstr = strsep(&stringp, "\n");
02111 
02112       fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
02113       
02114       fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
02115       while ((tempstr = strsep(&stringp, "\n")) != NULL)
02116          fprintf(htmlfile, "%s<BR>\n",tempstr);
02117       fprintf(htmlfile, "</TD></TR>\n");
02118       fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
02119 
02120    }
02121 
02122    fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
02123    fclose(htmlfile);
02124    ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
02125    return RESULT_SUCCESS;
02126 }
02127 
02128 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
02129 {
02130    enum agi_result res;
02131    struct ast_module_user *u;
02132    char *argv[MAX_ARGS];
02133    char buf[AGI_BUF_LEN] = "";
02134    char *tmp = (char *)buf;
02135    int argc = 0;
02136    int fds[2];
02137    int efd = -1;
02138    int pid;
02139    char *stringp;
02140    AGI agi;
02141 
02142    if (ast_strlen_zero(data)) {
02143       ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
02144       return -1;
02145    }
02146    ast_copy_string(buf, data, sizeof(buf));
02147 
02148    memset(&agi, 0, sizeof(agi));
02149         while ((stringp = strsep(&tmp, "|")) && argc < MAX_ARGS-1)
02150       argv[argc++] = stringp;
02151    argv[argc] = NULL;
02152 
02153    u = ast_module_user_add(chan);
02154 #if 0
02155     /* Answer if need be */
02156         if (chan->_state != AST_STATE_UP) {
02157       if (ast_answer(chan)) {
02158          LOCAL_USER_REMOVE(u);
02159          return -1;
02160       }
02161    }
02162 #endif
02163    ast_replace_sigchld();
02164    res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
02165    if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
02166       int status = 0;
02167       agi.fd = fds[1];
02168       agi.ctrl = fds[0];
02169       agi.audio = efd;
02170       agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
02171       res = run_agi(chan, argv[0], &agi, pid, &status, dead);
02172       /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
02173       if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
02174          res = AGI_RESULT_FAILURE;
02175       if (fds[1] != fds[0])
02176          close(fds[1]);
02177       if (efd > -1)
02178          close(efd);
02179    }
02180    ast_unreplace_sigchld();
02181    ast_module_user_remove(u);
02182 
02183    switch (res) {
02184    case AGI_RESULT_SUCCESS:
02185    case AGI_RESULT_SUCCESS_FAST:
02186       pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
02187       break;
02188    case AGI_RESULT_FAILURE:
02189       pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
02190       break;
02191    case AGI_RESULT_HANGUP:
02192       pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
02193       return -1;
02194    }
02195 
02196    return 0;
02197 }
02198 
02199 static int agi_exec(struct ast_channel *chan, void *data)
02200 {
02201    if (chan->_softhangup)
02202       ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
02203    return agi_exec_full(chan, data, 0, 0);
02204 }
02205 
02206 static int eagi_exec(struct ast_channel *chan, void *data)
02207 {
02208    int readformat;
02209    int res;
02210 
02211    if (chan->_softhangup)
02212       ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
02213    readformat = chan->readformat;
02214    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
02215       ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
02216       return -1;
02217    }
02218    res = agi_exec_full(chan, data, 1, 0);
02219    if (!res) {
02220       if (ast_set_read_format(chan, readformat)) {
02221          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
02222       }
02223    }
02224    return res;
02225 }
02226 
02227 static int deadagi_exec(struct ast_channel *chan, void *data)
02228 {
02229    if (!ast_check_hangup(chan))
02230       ast_log(LOG_WARNING,"Running DeadAGI on a live channel will cause problems, please use AGI\n");
02231    return agi_exec_full(chan, data, 0, 1);
02232 }
02233 
02234 static char showagi_help[] =
02235 "Usage: agi show [topic]\n"
02236 "       When called with a topic as an argument, displays usage\n"
02237 "       information on the given command.  If called without a\n"
02238 "       topic, it provides a list of AGI commands.\n";
02239 
02240 
02241 static char dumpagihtml_help[] =
02242 "Usage: agi dumphtml <filename>\n"
02243 "  Dumps the agi command list in html format to given filename\n";
02244 
02245 static struct ast_cli_entry cli_show_agi_deprecated = {
02246    { "show", "agi", NULL },
02247    handle_showagi, NULL,
02248    NULL };
02249 
02250 static struct ast_cli_entry cli_dump_agihtml_deprecated = {
02251    { "dump", "agihtml", NULL },
02252    handle_agidumphtml, NULL,
02253    NULL };
02254 
02255 static struct ast_cli_entry cli_agi_no_debug_deprecated = {
02256    { "agi", "no", "debug", NULL },
02257    agi_no_debug_deprecated, NULL,
02258    NULL };
02259 
02260 static struct ast_cli_entry cli_agi[] = {
02261    { { "agi", "debug", NULL },
02262    agi_do_debug, "Enable AGI debugging",
02263    debug_usage },
02264 
02265    { { "agi", "debug", "off", NULL },
02266    agi_no_debug, "Disable AGI debugging",
02267    no_debug_usage, NULL, &cli_agi_no_debug_deprecated },
02268 
02269    { { "agi", "show", NULL },
02270    handle_showagi, "List AGI commands or specific help",
02271    showagi_help, NULL, &cli_show_agi_deprecated },
02272 
02273    { { "agi", "dumphtml", NULL },
02274    handle_agidumphtml, "Dumps a list of agi commands in html format",
02275    dumpagihtml_help, NULL, &cli_dump_agihtml_deprecated },
02276 };
02277 
02278 static void *shaun_of_the_dead(void *data)
02279 {
02280    struct zombie *cur;
02281    int status;
02282    for (;;) {
02283       if (!AST_LIST_EMPTY(&zombies)) {
02284          /* Don't allow cancellation while we have a lock. */
02285          pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
02286          AST_LIST_LOCK(&zombies);
02287          AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
02288             if (waitpid(cur->pid, &status, WNOHANG) != 0) {
02289                AST_LIST_REMOVE_CURRENT(&zombies, list);
02290                ast_free(cur);
02291             }
02292          }
02293          AST_LIST_TRAVERSE_SAFE_END
02294          AST_LIST_UNLOCK(&zombies);
02295          pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
02296       }
02297       pthread_testcancel();
02298       /* Wait for 60 seconds, without engaging in a busy loop. */
02299       ast_poll(NULL, 0, 60000);
02300    }
02301    return NULL;
02302 }
02303 
02304 static int unload_module(void)
02305 {
02306    int res;
02307    struct zombie *cur;
02308    ast_module_user_hangup_all();
02309    ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
02310    ast_unregister_application(eapp);
02311    ast_unregister_application(deadapp);
02312    res = ast_unregister_application(app);
02313    if (shaun_of_the_dead_thread != AST_PTHREADT_NULL) {
02314       pthread_cancel(shaun_of_the_dead_thread);
02315       pthread_kill(shaun_of_the_dead_thread, SIGURG);
02316       pthread_join(shaun_of_the_dead_thread, NULL);
02317    }
02318    while ((cur = AST_LIST_REMOVE_HEAD(&zombies, list))) {
02319       ast_free(cur);
02320    }
02321    return res;
02322 }
02323 
02324 static int load_module(void)
02325 {
02326    if (ast_pthread_create_background(&shaun_of_the_dead_thread, NULL, shaun_of_the_dead, NULL)) {
02327       ast_log(LOG_ERROR, "Shaun of the Dead wants to kill zombies, but can't?!!\n");
02328       shaun_of_the_dead_thread = AST_PTHREADT_NULL;
02329    }
02330    ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
02331    ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
02332    ast_register_application(eapp, eagi_exec, esynopsis, descrip);
02333    return ast_register_application(app, agi_exec, synopsis, descrip);
02334 }
02335 
02336 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
02337                 .load = load_module,
02338                 .unload = unload_module,
02339       );

Generated on Sat Aug 6 00:39:31 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7