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