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