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