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