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