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