00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 172438 $")
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
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
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
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);
00152 res = ast_carefulwrite(fd, stuff, strlen(stuff), 100);
00153 free(stuff);
00154 }
00155
00156 return res;
00157 }
00158
00159
00160
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
00175 host = ast_strdupa(agiurl + 6);
00176
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 = 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
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
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
00323 ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
00324 }
00325 cap_free(cap);
00326 #endif
00327
00328
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
00342 ast_set_priority(0);
00343
00344
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
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
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
00369 for (x=STDERR_FILENO + 2;x<1024;x++)
00370 close(x);
00371
00372
00373 execv(script, argv);
00374
00375 fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno));
00376
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
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
00403
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
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
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
00427 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
00428
00429
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
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], "%d", &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
00464
00465
00466
00467
00468
00469
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], "%d", &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], "%ld", &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
00630
00631 sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
00632 ast_stopstream(chan);
00633 if (res == 1) {
00634
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
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
00663 timeout = chan->pbx->dtimeout * 1000;
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
00691
00692 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
00693 ast_stopstream(chan);
00694 if (res == 1) {
00695
00696 return RESULT_SUCCESS;
00697 }
00698
00699
00700 if (res == 0 ) {
00701 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
00702
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
00715
00716
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], "%d", &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], "%d", &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)
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)
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], "%d", &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], "%d", &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
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)
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)
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], "%d", &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;
00915 int totalsilence = 0;
00916 int dspsilence = 0;
00917 int silence = 0;
00918 int gotsilence = 0;
00919 char *silencestr=NULL;
00920 int rfmt=0;
00921
00922
00923
00924
00925 if (argc < 6)
00926 return RESULT_SHOWUSAGE;
00927 if (sscanf(argv[5], "%d", &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
00966
00967
00968 if ((argc >6) && (sscanf(argv[6], "%ld", &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
00989 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00990
00991 chan->stream = fs;
00992 ast_applystream(chan,fs);
00993
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
01019
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
01035
01036
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
01048 gotsilence = 1;
01049 break;
01050 }
01051 }
01052 break;
01053 case AST_FRAME_VIDEO:
01054 ast_writestream(fs, f);
01055 default:
01056
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], "%d", &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
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
01110 c = ast_get_channel_by_name_locked(argv[1]);
01111 if (c) {
01112
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
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;
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 res = pbx_exec(chan, app, argv[2]);
01144 } else {
01145 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01146 res = -2;
01147 }
01148 fdprintf(agi->fd, "200 result=%d\n", res);
01149
01150
01151 return res;
01152 }
01153
01154 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01155 {
01156 char tmp[256]="";
01157 char *l = NULL, *n = NULL;
01158
01159 if (argv[2]) {
01160 ast_copy_string(tmp, argv[2], sizeof(tmp));
01161 ast_callerid_parse(tmp, &n, &l);
01162 if (l)
01163 ast_shrink_phone_number(l);
01164 else
01165 l = "";
01166 if (!n)
01167 n = "";
01168 ast_set_callerid(chan, l, n, NULL);
01169 }
01170
01171 fdprintf(agi->fd, "200 result=1\n");
01172 return RESULT_SUCCESS;
01173 }
01174
01175 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01176 {
01177 struct ast_channel *c;
01178 if (argc == 2) {
01179
01180 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
01181 return RESULT_SUCCESS;
01182 } else if (argc == 3) {
01183
01184 c = ast_get_channel_by_name_locked(argv[2]);
01185 if (c) {
01186 fdprintf(agi->fd, "200 result=%d\n", c->_state);
01187 ast_channel_unlock(c);
01188 return RESULT_SUCCESS;
01189 }
01190
01191 fdprintf(agi->fd, "200 result=-1\n");
01192 return RESULT_SUCCESS;
01193 } else {
01194 return RESULT_SHOWUSAGE;
01195 }
01196 }
01197
01198 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01199 {
01200 if (argv[3])
01201 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01202
01203 fdprintf(agi->fd, "200 result=1\n");
01204 return RESULT_SUCCESS;
01205 }
01206
01207 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01208 {
01209 char *ret;
01210 char tempstr[1024];
01211
01212 if (argc != 3)
01213 return RESULT_SHOWUSAGE;
01214
01215
01216 if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01217 ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01218 } else {
01219 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01220 }
01221
01222 if (ret)
01223 fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
01224 else
01225 fdprintf(agi->fd, "200 result=0\n");
01226
01227 return RESULT_SUCCESS;
01228 }
01229
01230 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01231 {
01232 char tmp[4096] = "";
01233 struct ast_channel *chan2=NULL;
01234
01235 if ((argc != 4) && (argc != 5))
01236 return RESULT_SHOWUSAGE;
01237 if (argc == 5) {
01238 chan2 = ast_get_channel_by_name_locked(argv[4]);
01239 } else {
01240 chan2 = chan;
01241 }
01242 if (chan2) {
01243 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01244 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
01245 } else {
01246 fdprintf(agi->fd, "200 result=0\n");
01247 }
01248 if (chan2 && (chan2 != chan))
01249 ast_channel_unlock(chan2);
01250 return RESULT_SUCCESS;
01251 }
01252
01253 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01254 {
01255 int level = 0;
01256 char *prefix;
01257
01258 if (argc < 2)
01259 return RESULT_SHOWUSAGE;
01260
01261 if (argv[2])
01262 sscanf(argv[2], "%d", &level);
01263
01264 switch (level) {
01265 case 4:
01266 prefix = VERBOSE_PREFIX_4;
01267 break;
01268 case 3:
01269 prefix = VERBOSE_PREFIX_3;
01270 break;
01271 case 2:
01272 prefix = VERBOSE_PREFIX_2;
01273 break;
01274 case 1:
01275 default:
01276 prefix = VERBOSE_PREFIX_1;
01277 break;
01278 }
01279
01280 if (level <= option_verbose)
01281 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
01282
01283 fdprintf(agi->fd, "200 result=1\n");
01284
01285 return RESULT_SUCCESS;
01286 }
01287
01288 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01289 {
01290 int res;
01291 size_t bufsize = 16;
01292 char *buf, *tmp;
01293
01294 if (argc != 4)
01295 return RESULT_SHOWUSAGE;
01296
01297 if (!(buf = ast_malloc(bufsize))) {
01298 fdprintf(agi->fd, "200 result=-1\n");
01299 return RESULT_SUCCESS;
01300 }
01301
01302 do {
01303 res = ast_db_get(argv[2], argv[3], buf, bufsize);
01304 if (strlen(buf) < bufsize - 1) {
01305 break;
01306 }
01307 bufsize *= 2;
01308 if (!(tmp = ast_realloc(buf, bufsize))) {
01309 break;
01310 }
01311 buf = tmp;
01312 } while (1);
01313
01314 if (res)
01315 fdprintf(agi->fd, "200 result=0\n");
01316 else
01317 fdprintf(agi->fd, "200 result=1 (%s)\n", buf);
01318
01319 ast_free(buf);
01320 return RESULT_SUCCESS;
01321 }
01322
01323 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01324 {
01325 int res;
01326
01327 if (argc != 5)
01328 return RESULT_SHOWUSAGE;
01329 res = ast_db_put(argv[2], argv[3], argv[4]);
01330 fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01331 return RESULT_SUCCESS;
01332 }
01333
01334 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01335 {
01336 int res;
01337
01338 if (argc != 4)
01339 return RESULT_SHOWUSAGE;
01340 res = ast_db_del(argv[2], argv[3]);
01341 fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01342 return RESULT_SUCCESS;
01343 }
01344
01345 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01346 {
01347 int res;
01348 if ((argc < 3) || (argc > 4))
01349 return RESULT_SHOWUSAGE;
01350 if (argc == 4)
01351 res = ast_db_deltree(argv[2], argv[3]);
01352 else
01353 res = ast_db_deltree(argv[2], NULL);
01354
01355 fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1');
01356 return RESULT_SUCCESS;
01357 }
01358
01359 static char debug_usage[] =
01360 "Usage: agi debug\n"
01361 " Enables dumping of AGI transactions for debugging purposes\n";
01362
01363 static char no_debug_usage[] =
01364 "Usage: agi debug off\n"
01365 " Disables dumping of AGI transactions for debugging purposes\n";
01366
01367 static int agi_do_debug(int fd, int argc, char *argv[])
01368 {
01369 if (argc != 2)
01370 return RESULT_SHOWUSAGE;
01371 agidebug = 1;
01372 ast_cli(fd, "AGI Debugging Enabled\n");
01373 return RESULT_SUCCESS;
01374 }
01375
01376 static int agi_no_debug_deprecated(int fd, int argc, char *argv[])
01377 {
01378 if (argc != 3)
01379 return RESULT_SHOWUSAGE;
01380 agidebug = 0;
01381 ast_cli(fd, "AGI Debugging Disabled\n");
01382 return RESULT_SUCCESS;
01383 }
01384
01385 static int agi_no_debug(int fd, int argc, char *argv[])
01386 {
01387 if (argc != 3)
01388 return RESULT_SHOWUSAGE;
01389 agidebug = 0;
01390 ast_cli(fd, "AGI Debugging Disabled\n");
01391 return RESULT_SUCCESS;
01392 }
01393
01394 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01395 {
01396 fdprintf(agi->fd, "200 result=0\n");
01397 return RESULT_SUCCESS;
01398 }
01399
01400 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01401 {
01402 if (!strncasecmp(argv[2], "on", 2))
01403 ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01404 else if (!strncasecmp(argv[2], "off", 3))
01405 ast_moh_stop(chan);
01406 fdprintf(agi->fd, "200 result=0\n");
01407 return RESULT_SUCCESS;
01408 }
01409
01410 static char usage_setmusic[] =
01411 " Usage: SET MUSIC ON <on|off> <class>\n"
01412 " Enables/Disables the music on hold generator. If <class> is\n"
01413 " not specified, then the default music on hold class will be used.\n"
01414 " Always returns 0.\n";
01415
01416 static char usage_dbput[] =
01417 " Usage: DATABASE PUT <family> <key> <value>\n"
01418 " Adds or updates an entry in the Asterisk database for a\n"
01419 " given family, key, and value.\n"
01420 " Returns 1 if successful, 0 otherwise.\n";
01421
01422 static char usage_dbget[] =
01423 " Usage: DATABASE GET <family> <key>\n"
01424 " Retrieves an entry in the Asterisk database for a\n"
01425 " given family and key.\n"
01426 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
01427 " is set and returns the variable in parentheses.\n"
01428 " Example return code: 200 result=1 (testvariable)\n";
01429
01430 static char usage_dbdel[] =
01431 " Usage: DATABASE DEL <family> <key>\n"
01432 " Deletes an entry in the Asterisk database for a\n"
01433 " given family and key.\n"
01434 " Returns 1 if successful, 0 otherwise.\n";
01435
01436 static char usage_dbdeltree[] =
01437 " Usage: DATABASE DELTREE <family> [keytree]\n"
01438 " Deletes a family or specific keytree within a family\n"
01439 " in the Asterisk database.\n"
01440 " Returns 1 if successful, 0 otherwise.\n";
01441
01442 static char usage_verbose[] =
01443 " Usage: VERBOSE <message> <level>\n"
01444 " Sends <message> to the console via verbose message system.\n"
01445 " <level> is the the verbose level (1-4)\n"
01446 " Always returns 1.\n";
01447
01448 static char usage_getvariable[] =
01449 " Usage: GET VARIABLE <variablename>\n"
01450 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
01451 " is set and returns the variable in parentheses.\n"
01452 " example return code: 200 result=1 (testvariable)\n";
01453
01454 static char usage_getvariablefull[] =
01455 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
01456 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
01457 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
01458 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
01459 " example return code: 200 result=1 (testvariable)\n";
01460
01461 static char usage_setvariable[] =
01462 " Usage: SET VARIABLE <variablename> <value>\n";
01463
01464 static char usage_channelstatus[] =
01465 " Usage: CHANNEL STATUS [<channelname>]\n"
01466 " Returns the status of the specified channel.\n"
01467 " If no channel name is given the returns the status of the\n"
01468 " current channel. Return values:\n"
01469 " 0 Channel is down and available\n"
01470 " 1 Channel is down, but reserved\n"
01471 " 2 Channel is off hook\n"
01472 " 3 Digits (or equivalent) have been dialed\n"
01473 " 4 Line is ringing\n"
01474 " 5 Remote end is ringing\n"
01475 " 6 Line is up\n"
01476 " 7 Line is busy\n";
01477
01478 static char usage_setcallerid[] =
01479 " Usage: SET CALLERID <number>\n"
01480 " Changes the callerid of the current channel.\n";
01481
01482 static char usage_exec[] =
01483 " Usage: EXEC <application> <options>\n"
01484 " Executes <application> with given <options>.\n"
01485 " Returns whatever the application returns, or -2 on failure to find application\n";
01486
01487 static char usage_hangup[] =
01488 " Usage: HANGUP [<channelname>]\n"
01489 " Hangs up the specified channel.\n"
01490 " If no channel name is given, hangs up the current channel\n";
01491
01492 static char usage_answer[] =
01493 " Usage: ANSWER\n"
01494 " Answers channel if not already in answer state. Returns -1 on\n"
01495 " channel failure, or 0 if successful.\n";
01496
01497 static char usage_waitfordigit[] =
01498 " Usage: WAIT FOR DIGIT <timeout>\n"
01499 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
01500 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
01501 " the numerical value of the ascii of the digit if one is received. Use -1\n"
01502 " for the timeout value if you desire the call to block indefinitely.\n";
01503
01504 static char usage_sendtext[] =
01505 " Usage: SEND TEXT \"<text to send>\"\n"
01506 " Sends the given text on a channel. Most channels do not support the\n"
01507 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
01508 " support text transmission. Returns -1 only on error/hangup. Text\n"
01509 " consisting of greater than one word should be placed in quotes since the\n"
01510 " command only accepts a single argument.\n";
01511
01512 static char usage_recvchar[] =
01513 " Usage: RECEIVE CHAR <timeout>\n"
01514 " Receives a character of text on a channel. Specify timeout to be the\n"
01515 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
01516 " do not support the reception of text. Returns the decimal value of the character\n"
01517 " if one is received, or 0 if the channel does not support text reception. Returns\n"
01518 " -1 only on error/hangup.\n";
01519
01520 static char usage_recvtext[] =
01521 " Usage: RECEIVE TEXT <timeout>\n"
01522 " Receives a string of text on a channel. Specify timeout to be the\n"
01523 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
01524 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
01525
01526 static char usage_tddmode[] =
01527 " Usage: TDD MODE <on|off>\n"
01528 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
01529 " successful, or 0 if channel is not TDD-capable.\n";
01530
01531 static char usage_sendimage[] =
01532 " Usage: SEND IMAGE <image>\n"
01533 " Sends the given image on a channel. Most channels do not support the\n"
01534 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
01535 " support image transmission. Returns -1 only on error/hangup. Image names\n"
01536 " should not include extensions.\n";
01537
01538 static char usage_streamfile[] =
01539 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
01540 " Send the given file, allowing playback to be interrupted by the given\n"
01541 " digits, if any. Use double quotes for the digits if you wish none to be\n"
01542 " permitted. If sample offset is provided then the audio will seek to sample\n"
01543 " offset before play starts. Returns 0 if playback completes without a digit\n"
01544 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
01545 " or -1 on error or if the channel was disconnected. Remember, the file\n"
01546 " extension must not be included in the filename.\n";
01547
01548 static char usage_controlstreamfile[] =
01549 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
01550 " Send the given file, allowing playback to be controled by the given\n"
01551 " digits, if any. Use double quotes for the digits if you wish none to be\n"
01552 " permitted. 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\n"
01556 " Note: ffchar and rewchar default to * and # respectively.\n";
01557
01558 static char usage_getoption[] =
01559 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
01560 " Behaves similar to STREAM FILE but used with a timeout option.\n";
01561
01562 static char usage_saynumber[] =
01563 " Usage: SAY NUMBER <number> <escape digits>\n"
01564 " Say a given number, returning early if any of the given DTMF digits\n"
01565 " are received on the channel. Returns 0 if playback completes without a digit\n"
01566 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01567 " -1 on error/hangup.\n";
01568
01569 static char usage_saydigits[] =
01570 " Usage: SAY DIGITS <number> <escape digits>\n"
01571 " Say a given digit string, returning early if any of the given DTMF digits\n"
01572 " are received on the channel. Returns 0 if playback completes without a digit\n"
01573 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01574 " -1 on error/hangup.\n";
01575
01576 static char usage_sayalpha[] =
01577 " Usage: SAY ALPHA <number> <escape digits>\n"
01578 " Say a given character string, returning early if any of the given DTMF digits\n"
01579 " are received on the channel. Returns 0 if playback completes without a digit\n"
01580 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
01581 " -1 on error/hangup.\n";
01582
01583 static char usage_saydate[] =
01584 " Usage: SAY DATE <date> <escape digits>\n"
01585 " Say a given date, returning early if any of the given DTMF digits are\n"
01586 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
01587 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
01588 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01589 " digit if one was pressed or -1 on error/hangup.\n";
01590
01591 static char usage_saytime[] =
01592 " Usage: SAY TIME <time> <escape digits>\n"
01593 " Say a given time, returning early if any of the given DTMF digits are\n"
01594 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
01595 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
01596 " completes without a digit being pressed, or the ASCII numerical value of the\n"
01597 " digit if one was pressed or -1 on error/hangup.\n";
01598
01599 static char usage_saydatetime[] =
01600 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
01601 " Say a given time, returning early if any of the given DTMF digits are\n"
01602 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
01603 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
01604 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
01605 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
01606 " /usr/share/zoneinfo. Defaults to machine default. 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_sayphonetic[] =
01611 " Usage: SAY PHONETIC <string> <escape digits>\n"
01612 " Say a given character string with phonetics, returning early if any of the\n"
01613 " given DTMF digits are received on the channel. Returns 0 if playback\n"
01614 " completes without a digit pressed, the ASCII numerical value of the digit\n"
01615 " if one was pressed, or -1 on error/hangup.\n";
01616
01617 static char usage_getdata[] =
01618 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
01619 " Stream the given file, and recieve DTMF data. Returns the digits received\n"
01620 "from the channel at the other end.\n";
01621
01622 static char usage_setcontext[] =
01623 " Usage: SET CONTEXT <desired context>\n"
01624 " Sets the context for continuation upon exiting the application.\n";
01625
01626 static char usage_setextension[] =
01627 " Usage: SET EXTENSION <new extension>\n"
01628 " Changes the extension for continuation upon exiting the application.\n";
01629
01630 static char usage_setpriority[] =
01631 " Usage: SET PRIORITY <priority>\n"
01632 " Changes the priority for continuation upon exiting the application.\n"
01633 " The priority must be a valid priority or label.\n";
01634
01635 static char usage_recordfile[] =
01636 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
01637 " [offset samples] [BEEP] [s=silence]\n"
01638 " Record to a file until a given dtmf digit in the sequence is received\n"
01639 " Returns -1 on hangup or error. The format will specify what kind of file\n"
01640 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
01641 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
01642 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
01643 " of seconds of silence allowed before the function returns despite the\n"
01644 " lack of dtmf digits or reaching timeout. Silence value must be\n"
01645 " preceeded by \"s=\" and is also optional.\n";
01646
01647 static char usage_autohangup[] =
01648 " Usage: SET AUTOHANGUP <time>\n"
01649 " Cause the channel to automatically hangup at <time> seconds in the\n"
01650 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
01651 " cause the autohangup feature to be disabled on this channel.\n";
01652
01653 static char usage_noop[] =
01654 " Usage: NoOp\n"
01655 " Does nothing.\n";
01656
01657 static agi_command commands[MAX_COMMANDS] = {
01658 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
01659 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
01660 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
01661 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
01662 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
01663 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
01664 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
01665 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
01666 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
01667 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
01668 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
01669 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
01670 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
01671 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar },
01672 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext },
01673 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
01674 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
01675 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
01676 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
01677 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
01678 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate },
01679 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
01680 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime },
01681 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
01682 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
01683 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
01684 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
01685 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
01686 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
01687 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
01688 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
01689 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
01690 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
01691 { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile },
01692 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
01693 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
01694 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
01695 };
01696
01697 static int help_workhorse(int fd, char *match[])
01698 {
01699 char fullcmd[80];
01700 char matchstr[80];
01701 int x;
01702 struct agi_command *e;
01703 if (match)
01704 ast_join(matchstr, sizeof(matchstr), match);
01705 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
01706 e = &commands[x];
01707 if (!e->cmda[0])
01708 break;
01709
01710 if ((e->cmda[0])[0] == '_')
01711 continue;
01712 ast_join(fullcmd, sizeof(fullcmd), e->cmda);
01713 if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
01714 continue;
01715 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
01716 }
01717 return 0;
01718 }
01719
01720 int ast_agi_register(agi_command *agi)
01721 {
01722 int x;
01723 for (x=0; x<MAX_COMMANDS - 1; x++) {
01724 if (commands[x].cmda[0] == agi->cmda[0]) {
01725 ast_log(LOG_WARNING, "Command already registered!\n");
01726 return -1;
01727 }
01728 }
01729 for (x=0; x<MAX_COMMANDS - 1; x++) {
01730 if (!commands[x].cmda[0]) {
01731 commands[x] = *agi;
01732 return 0;
01733 }
01734 }
01735 ast_log(LOG_WARNING, "No more room for new commands!\n");
01736 return -1;
01737 }
01738
01739 void ast_agi_unregister(agi_command *agi)
01740 {
01741 int x;
01742 for (x=0; x<MAX_COMMANDS - 1; x++) {
01743 if (commands[x].cmda[0] == agi->cmda[0]) {
01744 memset(&commands[x], 0, sizeof(agi_command));
01745 }
01746 }
01747 }
01748
01749 static agi_command *find_command(char *cmds[], int exact)
01750 {
01751 int x;
01752 int y;
01753 int match;
01754
01755 for (x=0; x < sizeof(commands) / sizeof(commands[0]); x++) {
01756 if (!commands[x].cmda[0])
01757 break;
01758
01759 match = 1;
01760 for (y=0; match && cmds[y]; y++) {
01761
01762
01763
01764 if (!commands[x].cmda[y] && !exact)
01765 break;
01766
01767 if (!commands[x].cmda[y])
01768 return NULL;
01769 if (strcasecmp(commands[x].cmda[y], cmds[y]))
01770 match = 0;
01771 }
01772
01773
01774 if ((exact > -1) && commands[x].cmda[y])
01775 match = 0;
01776 if (match)
01777 return &commands[x];
01778 }
01779 return NULL;
01780 }
01781
01782
01783 static int parse_args(char *s, int *max, char *argv[])
01784 {
01785 int x=0;
01786 int quoted=0;
01787 int escaped=0;
01788 int whitespace=1;
01789 char *cur;
01790
01791 cur = s;
01792 while(*s) {
01793 switch(*s) {
01794 case '"':
01795
01796 if (escaped)
01797 goto normal;
01798 else
01799 quoted = !quoted;
01800 if (quoted && whitespace) {
01801
01802 argv[x++] = cur;
01803 whitespace=0;
01804 }
01805 escaped = 0;
01806 break;
01807 case ' ':
01808 case '\t':
01809 if (!quoted && !escaped) {
01810
01811
01812 whitespace = 1;
01813 *(cur++) = '\0';
01814 } else
01815
01816 goto normal;
01817 break;
01818 case '\\':
01819
01820 if (escaped) {
01821 goto normal;
01822 } else {
01823 escaped=1;
01824 }
01825 break;
01826 default:
01827 normal:
01828 if (whitespace) {
01829 if (x >= MAX_ARGS -1) {
01830 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
01831 break;
01832 }
01833
01834 argv[x++] = cur;
01835 whitespace=0;
01836 }
01837 *(cur++) = *s;
01838 escaped=0;
01839 }
01840 s++;
01841 }
01842
01843 *(cur++) = '\0';
01844 argv[x] = NULL;
01845 *max = x;
01846 return 0;
01847 }
01848
01849 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
01850 {
01851 char *argv[MAX_ARGS];
01852 int argc = MAX_ARGS;
01853 int res;
01854 agi_command *c;
01855
01856 parse_args(buf, &argc, argv);
01857 c = find_command(argv, 0);
01858 if (c) {
01859
01860
01861 if (chan->cdr && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
01862 ast_cdr_setapp(chan->cdr, "AGI", buf);
01863
01864 res = c->handler(chan, agi, argc, argv);
01865 switch(res) {
01866 case RESULT_SHOWUSAGE:
01867 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
01868 fdprintf(agi->fd, "%s", c->usage);
01869 fdprintf(agi->fd, "520 End of proper usage.\n");
01870 break;
01871 case AST_PBX_KEEPALIVE:
01872
01873 return AST_PBX_KEEPALIVE;
01874 break;
01875 case RESULT_FAILURE:
01876
01877
01878 return -1;
01879 }
01880 } else {
01881 fdprintf(agi->fd, "510 Invalid or unknown command\n");
01882 }
01883 return 0;
01884 }
01885 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead)
01886 {
01887 struct ast_channel *c;
01888 int outfd;
01889 int ms;
01890 enum agi_result returnstatus = AGI_RESULT_SUCCESS;
01891 struct ast_frame *f;
01892 char buf[AGI_BUF_LEN];
01893 char *res = NULL;
01894 FILE *readf;
01895
01896
01897 int retry = AGI_NANDFS_RETRY;
01898
01899 if (!(readf = fdopen(agi->ctrl, "r"))) {
01900 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
01901 if (pid > -1)
01902 kill(pid, SIGHUP);
01903 close(agi->ctrl);
01904 return AGI_RESULT_FAILURE;
01905 }
01906 setlinebuf(readf);
01907 setup_env(chan, request, agi->fd, (agi->audio > -1));
01908 for (;;) {
01909 ms = -1;
01910 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
01911 if (c) {
01912 retry = AGI_NANDFS_RETRY;
01913
01914 f = ast_read(c);
01915 if (!f) {
01916 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
01917 returnstatus = AGI_RESULT_HANGUP;
01918 break;
01919 } else {
01920
01921 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
01922
01923 if (write(agi->audio, f->data, f->datalen) < 0) {
01924 }
01925 }
01926 ast_frfree(f);
01927 }
01928 } else if (outfd > -1) {
01929 size_t len = sizeof(buf);
01930 size_t buflen = 0;
01931
01932 retry = AGI_NANDFS_RETRY;
01933 buf[0] = '\0';
01934
01935 while (buflen < (len - 1)) {
01936 res = fgets(buf + buflen, len, readf);
01937 if (feof(readf))
01938 break;
01939 if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))
01940 break;
01941 if (res != NULL && !agi->fast)
01942 break;
01943 buflen = strlen(buf);
01944 if (buflen && buf[buflen - 1] == '\n')
01945 break;
01946 len -= buflen;
01947 if (agidebug)
01948 ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
01949 }
01950
01951 if (!buf[0]) {
01952
01953 if (returnstatus)
01954 returnstatus = -1;
01955 if (option_verbose > 2)
01956 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
01957 if (pid > 0)
01958 waitpid(pid, status, 0);
01959
01960 pid = -1;
01961 break;
01962 }
01963
01964
01965 if (*buf && strncasecmp(buf, "failure", 7) == 0) {
01966 returnstatus = AGI_RESULT_FAILURE;
01967 break;
01968 }
01969
01970
01971 if (*buf && buf[strlen(buf) - 1] == '\n')
01972 buf[strlen(buf) - 1] = 0;
01973 if (agidebug)
01974 ast_verbose("AGI Rx << %s\n", buf);
01975 returnstatus |= agi_handle_command(chan, agi, buf);
01976
01977 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
01978 break;
01979 }
01980 } else {
01981 if (--retry <= 0) {
01982 ast_log(LOG_WARNING, "No channel, no fd?\n");
01983 returnstatus = AGI_RESULT_FAILURE;
01984 break;
01985 }
01986 }
01987 }
01988
01989 if (pid > -1) {
01990 const char *sighup = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
01991 if (ast_strlen_zero(sighup) || !ast_false(sighup)) {
01992 if (kill(pid, SIGHUP)) {
01993 ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
01994 } else {
01995 usleep(1);
01996 }
01997 }
01998
01999
02000
02001
02002
02003
02004
02005
02006
02007
02008
02009 if (waitpid(pid, status, WNOHANG) == 0) {
02010 struct zombie *cur = ast_calloc(1, sizeof(*cur));
02011 if (cur) {
02012 cur->pid = pid;
02013 AST_LIST_LOCK(&zombies);
02014 AST_LIST_INSERT_TAIL(&zombies, cur, list);
02015 AST_LIST_UNLOCK(&zombies);
02016 }
02017 }
02018 }
02019 fclose(readf);
02020 return returnstatus;
02021 }
02022
02023 static int handle_showagi(int fd, int argc, char *argv[])
02024 {
02025 struct agi_command *e;
02026 char fullcmd[80];
02027 if ((argc < 2))
02028 return RESULT_SHOWUSAGE;
02029 if (argc > 2) {
02030 e = find_command(argv + 2, 1);
02031 if (e)
02032 ast_cli(fd, "%s", e->usage);
02033 else {
02034 if (find_command(argv + 2, -1)) {
02035 return help_workhorse(fd, argv + 1);
02036 } else {
02037 ast_join(fullcmd, sizeof(fullcmd), argv+1);
02038 ast_cli(fd, "No such command '%s'.\n", fullcmd);
02039 }
02040 }
02041 } else {
02042 return help_workhorse(fd, NULL);
02043 }
02044 return RESULT_SUCCESS;
02045 }
02046
02047 static int handle_agidumphtml(int fd, int argc, char *argv[])
02048 {
02049 struct agi_command *e;
02050 char fullcmd[80];
02051 int x;
02052 FILE *htmlfile;
02053
02054 if ((argc < 3))
02055 return RESULT_SHOWUSAGE;
02056
02057 if (!(htmlfile = fopen(argv[2], "wt"))) {
02058 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
02059 return RESULT_SHOWUSAGE;
02060 }
02061
02062 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
02063 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
02064
02065
02066 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
02067
02068 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
02069 char *stringp, *tempstr;
02070
02071 e = &commands[x];
02072 if (!e->cmda[0])
02073 break;
02074
02075 if ((e->cmda[0])[0] == '_')
02076 continue;
02077 ast_join(fullcmd, sizeof(fullcmd), e->cmda);
02078
02079 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
02080 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd,e->summary);
02081
02082 stringp=e->usage;
02083 tempstr = strsep(&stringp, "\n");
02084
02085 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
02086
02087 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
02088 while ((tempstr = strsep(&stringp, "\n")) != NULL)
02089 fprintf(htmlfile, "%s<BR>\n",tempstr);
02090 fprintf(htmlfile, "</TD></TR>\n");
02091 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
02092
02093 }
02094
02095 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
02096 fclose(htmlfile);
02097 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
02098 return RESULT_SUCCESS;
02099 }
02100
02101 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
02102 {
02103 enum agi_result res;
02104 struct ast_module_user *u;
02105 char *argv[MAX_ARGS];
02106 char buf[AGI_BUF_LEN] = "";
02107 char *tmp = (char *)buf;
02108 int argc = 0;
02109 int fds[2];
02110 int efd = -1;
02111 int pid;
02112 char *stringp;
02113 AGI agi;
02114
02115 if (ast_strlen_zero(data)) {
02116 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
02117 return -1;
02118 }
02119 ast_copy_string(buf, data, sizeof(buf));
02120
02121 memset(&agi, 0, sizeof(agi));
02122 while ((stringp = strsep(&tmp, "|")) && argc < MAX_ARGS-1)
02123 argv[argc++] = stringp;
02124 argv[argc] = NULL;
02125
02126 u = ast_module_user_add(chan);
02127 #if 0
02128
02129 if (chan->_state != AST_STATE_UP) {
02130 if (ast_answer(chan)) {
02131 LOCAL_USER_REMOVE(u);
02132 return -1;
02133 }
02134 }
02135 #endif
02136 ast_replace_sigchld();
02137 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
02138 if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
02139 int status = 0;
02140 agi.fd = fds[1];
02141 agi.ctrl = fds[0];
02142 agi.audio = efd;
02143 agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
02144 res = run_agi(chan, argv[0], &agi, pid, &status, dead);
02145
02146 if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
02147 res = AGI_RESULT_FAILURE;
02148 if (fds[1] != fds[0])
02149 close(fds[1]);
02150 if (efd > -1)
02151 close(efd);
02152 }
02153 ast_unreplace_sigchld();
02154 ast_module_user_remove(u);
02155
02156 switch (res) {
02157 case AGI_RESULT_SUCCESS:
02158 case AGI_RESULT_SUCCESS_FAST:
02159 pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
02160 break;
02161 case AGI_RESULT_FAILURE:
02162 pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
02163 break;
02164 case AGI_RESULT_HANGUP:
02165 pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
02166 return -1;
02167 }
02168
02169 return 0;
02170 }
02171
02172 static int agi_exec(struct ast_channel *chan, void *data)
02173 {
02174 if (chan->_softhangup)
02175 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
02176 return agi_exec_full(chan, data, 0, 0);
02177 }
02178
02179 static int eagi_exec(struct ast_channel *chan, void *data)
02180 {
02181 int readformat;
02182 int res;
02183
02184 if (chan->_softhangup)
02185 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
02186 readformat = chan->readformat;
02187 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
02188 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
02189 return -1;
02190 }
02191 res = agi_exec_full(chan, data, 1, 0);
02192 if (!res) {
02193 if (ast_set_read_format(chan, readformat)) {
02194 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
02195 }
02196 }
02197 return res;
02198 }
02199
02200 static int deadagi_exec(struct ast_channel *chan, void *data)
02201 {
02202 if (!ast_check_hangup(chan))
02203 ast_log(LOG_WARNING,"Running DeadAGI on a live channel will cause problems, please use AGI\n");
02204 return agi_exec_full(chan, data, 0, 1);
02205 }
02206
02207 static char showagi_help[] =
02208 "Usage: agi show [topic]\n"
02209 " When called with a topic as an argument, displays usage\n"
02210 " information on the given command. If called without a\n"
02211 " topic, it provides a list of AGI commands.\n";
02212
02213
02214 static char dumpagihtml_help[] =
02215 "Usage: agi dumphtml <filename>\n"
02216 " Dumps the agi command list in html format to given filename\n";
02217
02218 static struct ast_cli_entry cli_show_agi_deprecated = {
02219 { "show", "agi", NULL },
02220 handle_showagi, NULL,
02221 NULL };
02222
02223 static struct ast_cli_entry cli_dump_agihtml_deprecated = {
02224 { "dump", "agihtml", NULL },
02225 handle_agidumphtml, NULL,
02226 NULL };
02227
02228 static struct ast_cli_entry cli_agi_no_debug_deprecated = {
02229 { "agi", "no", "debug", NULL },
02230 agi_no_debug_deprecated, NULL,
02231 NULL };
02232
02233 static struct ast_cli_entry cli_agi[] = {
02234 { { "agi", "debug", NULL },
02235 agi_do_debug, "Enable AGI debugging",
02236 debug_usage },
02237
02238 { { "agi", "debug", "off", NULL },
02239 agi_no_debug, "Disable AGI debugging",
02240 no_debug_usage, NULL, &cli_agi_no_debug_deprecated },
02241
02242 { { "agi", "show", NULL },
02243 handle_showagi, "List AGI commands or specific help",
02244 showagi_help, NULL, &cli_show_agi_deprecated },
02245
02246 { { "agi", "dumphtml", NULL },
02247 handle_agidumphtml, "Dumps a list of agi commands in html format",
02248 dumpagihtml_help, NULL, &cli_dump_agihtml_deprecated },
02249 };
02250
02251 static void *shaun_of_the_dead(void *data)
02252 {
02253 struct zombie *cur;
02254 int status;
02255 for (;;) {
02256 if (!AST_LIST_EMPTY(&zombies)) {
02257
02258 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
02259 AST_LIST_LOCK(&zombies);
02260 AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
02261 if (waitpid(cur->pid, &status, WNOHANG) != 0) {
02262 AST_LIST_REMOVE_CURRENT(&zombies, list);
02263 ast_free(cur);
02264 }
02265 }
02266 AST_LIST_TRAVERSE_SAFE_END
02267 AST_LIST_UNLOCK(&zombies);
02268 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
02269 }
02270 pthread_testcancel();
02271
02272 poll(NULL, 0, 60000);
02273 }
02274 return NULL;
02275 }
02276
02277 static int unload_module(void)
02278 {
02279 int res;
02280 struct zombie *cur;
02281 ast_module_user_hangup_all();
02282 ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
02283 ast_unregister_application(eapp);
02284 ast_unregister_application(deadapp);
02285 res = ast_unregister_application(app);
02286 if (shaun_of_the_dead_thread != AST_PTHREADT_NULL) {
02287 pthread_cancel(shaun_of_the_dead_thread);
02288 pthread_kill(shaun_of_the_dead_thread, SIGURG);
02289 pthread_join(shaun_of_the_dead_thread, NULL);
02290 }
02291 while ((cur = AST_LIST_REMOVE_HEAD(&zombies, list))) {
02292 ast_free(cur);
02293 }
02294 return res;
02295 }
02296
02297 static int load_module(void)
02298 {
02299 if (ast_pthread_create_background(&shaun_of_the_dead_thread, NULL, shaun_of_the_dead, NULL)) {
02300 ast_log(LOG_ERROR, "Shaun of the Dead wants to kill zombies, but can't?!!\n");
02301 shaun_of_the_dead_thread = AST_PTHREADT_NULL;
02302 }
02303 ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
02304 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
02305 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
02306 return ast_register_application(app, agi_exec, synopsis, descrip);
02307 }
02308
02309 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
02310 .load = load_module,
02311 .unload = unload_module,
02312 );