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