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