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: 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
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 = 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
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], "%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
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], "%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
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], "%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)
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], "%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
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], "%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;
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], "%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
00968
00969
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
00991 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00992
00993 chan->stream = fs;
00994 ast_applystream(chan,fs);
00995
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
01021
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
01037
01038
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
01050 gotsilence = 1;
01051 break;
01052 }
01053 }
01054 break;
01055 case AST_FRAME_VIDEO:
01056 ast_writestream(fs, f);
01057 default:
01058
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
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
01112 c = ast_get_channel_by_name_locked(argv[1]);
01113 if (c) {
01114
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
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
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
01188 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
01189 return RESULT_SUCCESS;
01190 } else if (argc == 3) {
01191
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
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
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
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
01770 match = 1;
01771 for (y=0; match && cmds[y]; y++) {
01772
01773
01774
01775 if (!commands[x].cmda[y] && !exact)
01776 break;
01777
01778 if (!commands[x].cmda[y])
01779 return NULL;
01780 if (strcasecmp(commands[x].cmda[y], cmds[y]))
01781 match = 0;
01782 }
01783
01784
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
01807 if (escaped)
01808 goto normal;
01809 else
01810 quoted = !quoted;
01811 if (quoted && whitespace) {
01812
01813 argv[x++] = cur;
01814 whitespace=0;
01815 }
01816 escaped = 0;
01817 break;
01818 case ' ':
01819 case '\t':
01820 if (!quoted && !escaped) {
01821
01822
01823 whitespace = 1;
01824 *(cur++) = '\0';
01825 } else
01826
01827 goto normal;
01828 break;
01829 case '\\':
01830
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
01845 argv[x++] = cur;
01846 whitespace=0;
01847 }
01848 *(cur++) = *s;
01849 escaped=0;
01850 }
01851 s++;
01852 }
01853
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
01871
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
01884 return AST_PBX_KEEPALIVE;
01885 break;
01886 case RESULT_FAILURE:
01887
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
01908
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
01929
01930
01931 c = chan;
01932 }
01933 if (c) {
01934 retry = AGI_NANDFS_RETRY;
01935
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
01943 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
01944
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
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
01981 pid = -1;
01982 break;
01983 }
01984
01985
01986 if (*buf && strncasecmp(buf, "failure", 7) == 0) {
01987 returnstatus = AGI_RESULT_FAILURE;
01988 break;
01989 }
01990
01991
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
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
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 {
02022 usleep(1);
02023 }
02024 }
02025
02026
02027
02028
02029
02030
02031
02032
02033
02034
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])
02100 break;
02101
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
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
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
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
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 );