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