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