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