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