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