2 * Asterisk -- A telephony toolkit for Linux.
4 * Asterisk Gateway Interface
6 * Copyright (C) 1999 - 2005, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <netinet/tcp.h>
19 #include <arpa/inet.h>
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35 #include "asterisk/file.h"
36 #include "asterisk/logger.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/astdb.h"
41 #include "asterisk/callerid.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/options.h"
45 #include "asterisk/image.h"
46 #include "asterisk/say.h"
47 #include "asterisk/app.h"
48 #include "asterisk/dsp.h"
49 #include "asterisk/musiconhold.h"
50 #include "asterisk/manager.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/agi.h"
55 #include "asterisk/astmm.h"
59 #define MAX_COMMANDS 128
61 /* Recycle some stuff from the CLI interface */
62 #define fdprintf agi_debug_cli
64 static char *tdesc = "Asterisk Gateway Interface (AGI)";
66 static char *app = "AGI";
68 static char *eapp = "EAGI";
70 static char *deadapp = "DeadAGI";
72 static char *synopsis = "Executes an AGI compliant application";
73 static char *esynopsis = "Executes an EAGI compliant application";
74 static char *deadsynopsis = "Executes AGI on a hungup channel";
76 static char *descrip =
77 " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
78 "program on a channel. AGI allows Asterisk to launch external programs\n"
79 "written in any language to control a telephony channel, play audio,\n"
80 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
82 "Returns -1 on hangup (except for DeadAGI) or if application requested\n"
83 " hangup, or 0 on non-hangup exit. \n"
84 "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
85 "on file descriptor 3\n\n"
86 "Use the CLI command 'show agi' to list available agi commands\n";
88 static int agidebug = 0;
95 #define TONE_BLOCK_SIZE 200
97 /* Max time to connect to an AGI remote host */
98 #define MAX_AGI_CONNECT 2000
100 #define AGI_PORT 4573
102 static void agi_debug_cli(int fd, char *fmt, ...)
109 res = vasprintf(&stuff, fmt, ap);
112 ast_log(LOG_ERROR, "Out of memory\n");
115 ast_verbose("AGI Tx >> %s", stuff);
116 ast_carefulwrite(fd, stuff, strlen(stuff), 100);
121 /* launch_netscript: The fastagi handler.
122 FastAGI defaults to port 4573 */
123 static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
127 struct pollfd pfds[1];
129 char *c; int port = AGI_PORT;
131 struct sockaddr_in sin;
133 struct ast_hostent ahp;
135 host = ast_strdupa(agiurl + 6); /* Remove agi:// */
138 /* Strip off any script name */
139 if ((c = strchr(host, '/'))) {
144 if ((c = strchr(host, ':'))) {
150 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
153 hp = ast_gethostbyname(host, &ahp);
155 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
158 s = socket(AF_INET, SOCK_STREAM, 0);
160 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
163 flags = fcntl(s, F_GETFL);
165 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
169 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
170 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
174 memset(&sin, 0, sizeof(sin));
175 sin.sin_family = AF_INET;
176 sin.sin_port = htons(port);
177 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
178 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
179 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
184 pfds[0].events = POLLOUT;
185 if (poll(pfds, 1, MAX_AGI_CONNECT) != 1) {
186 ast_log(LOG_WARNING, "Connect to '%s' failed!\n", agiurl);
190 if (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) {
191 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
196 /* If we have a script parameter, relay it to the fastagi server */
197 if (!ast_strlen_zero(script))
198 fdprintf(s, "agi_network_script: %s\n", script);
200 if (option_debug > 3)
201 ast_log(LOG_DEBUG, "Wow, connected!\n");
208 static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
218 if (!strncasecmp(script, "agi://", 6))
219 return launch_netscript(script, argv, fds, efd, opid);
221 if (script[0] != '/') {
222 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
226 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
230 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
237 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
244 res = fcntl(audio[1], F_GETFL);
246 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
248 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
260 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
264 /* Redirect stdin and out, provide enhanced audio channel if desired */
265 dup2(fromast[0], STDIN_FILENO);
266 dup2(toast[1], STDOUT_FILENO);
268 dup2(audio[0], STDERR_FILENO + 1);
270 close(STDERR_FILENO + 1);
272 /* Close everything but stdin/out/error */
273 for (x=STDERR_FILENO + 2;x<1024;x++)
277 /* Can't use ast_log since FD's are closed */
278 fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
281 if (option_verbose > 2)
282 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
288 /* close what we're not using in the parent */
302 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
304 /* Print initial environment, with agi_request always being the first
306 fdprintf(fd, "agi_request: %s\n", request);
307 fdprintf(fd, "agi_channel: %s\n", chan->name);
308 fdprintf(fd, "agi_language: %s\n", chan->language);
309 fdprintf(fd, "agi_type: %s\n", chan->type);
310 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
313 fdprintf(fd, "agi_callerid: %s\n", chan->cid.cid_num ? chan->cid.cid_num : "unknown");
314 fdprintf(fd, "agi_calleridname: %s\n", chan->cid.cid_name ? chan->cid.cid_name : "unknown");
315 fdprintf(fd, "agi_callingpres: %d\n", chan->cid.cid_pres);
316 fdprintf(fd, "agi_callingani2: %d\n", chan->cid.cid_ani2);
317 fdprintf(fd, "agi_callington: %d\n", chan->cid.cid_ton);
318 fdprintf(fd, "agi_callingtns: %d\n", chan->cid.cid_tns);
319 fdprintf(fd, "agi_dnid: %s\n", chan->cid.cid_dnid ? chan->cid.cid_dnid : "unknown");
320 fdprintf(fd, "agi_rdnis: %s\n", chan->cid.cid_rdnis ? chan->cid.cid_rdnis : "unknown");
322 /* Context information */
323 fdprintf(fd, "agi_context: %s\n", chan->context);
324 fdprintf(fd, "agi_extension: %s\n", chan->exten);
325 fdprintf(fd, "agi_priority: %d\n", chan->priority);
326 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
328 /* User information */
329 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
331 /* End with empty return */
335 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
339 if (chan->_state != AST_STATE_UP) {
340 /* Answer the chan */
341 res = ast_answer(chan);
343 fdprintf(agi->fd, "200 result=%d\n", res);
345 return RESULT_SUCCESS;
347 return RESULT_FAILURE;
350 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
355 return RESULT_SHOWUSAGE;
356 if (sscanf(argv[3], "%d", &to) != 1)
357 return RESULT_SHOWUSAGE;
358 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
359 fdprintf(agi->fd, "200 result=%d\n", res);
361 return RESULT_SUCCESS;
363 return RESULT_FAILURE;
366 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
370 return RESULT_SHOWUSAGE;
371 /* At the moment, the parser (perhaps broken) returns with
372 the last argument PLUS the newline at the end of the input
373 buffer. This probably needs to be fixed, but I wont do that
374 because other stuff may break as a result. The right way
375 would probably be to strip off the trailing newline before
376 parsing, then here, add a newline at the end of the string
377 before sending it to ast_sendtext --DUDE */
378 res = ast_sendtext(chan, argv[2]);
379 fdprintf(agi->fd, "200 result=%d\n", res);
381 return RESULT_SUCCESS;
383 return RESULT_FAILURE;
386 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
390 return RESULT_SHOWUSAGE;
391 res = ast_recvchar(chan,atoi(argv[2]));
393 fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
394 return RESULT_SUCCESS;
397 fdprintf(agi->fd, "200 result=%d\n", res);
398 return RESULT_SUCCESS;
401 fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
402 return RESULT_FAILURE;
406 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
411 return RESULT_SHOWUSAGE;
412 buf = ast_recvtext(chan,atoi(argv[2]));
414 fdprintf(agi->fd, "200 result=1 (%s)\n", buf);
416 return RESULT_SUCCESS;
418 fdprintf(agi->fd, "200 result=-1\n");
419 return RESULT_FAILURE;
423 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
427 return RESULT_SHOWUSAGE;
428 if (!strncasecmp(argv[2],"on",2))
432 if (!strncasecmp(argv[2],"mate",4))
434 if (!strncasecmp(argv[2],"tdd",3))
436 res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
437 if(res != RESULT_SUCCESS)
438 fdprintf(agi->fd, "200 result=0\n");
440 fdprintf(agi->fd, "200 result=1\n");
441 return RESULT_SUCCESS;
444 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
448 return RESULT_SHOWUSAGE;
449 res = ast_send_image(chan, argv[2]);
450 if (!ast_check_hangup(chan))
452 fdprintf(agi->fd, "200 result=%d\n", res);
454 return RESULT_SUCCESS;
456 return RESULT_FAILURE;
459 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
462 struct ast_filestream *fs;
463 long sample_offset = 0;
467 return RESULT_SHOWUSAGE;
469 return RESULT_SHOWUSAGE;
470 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
471 return RESULT_SHOWUSAGE;
473 fs = ast_openstream(chan, argv[2], chan->language);
475 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
476 return RESULT_SUCCESS;
478 ast_seekstream(fs, 0, SEEK_END);
479 max_length = ast_tellstream(fs);
480 ast_seekstream(fs, sample_offset, SEEK_SET);
481 res = ast_applystream(chan, fs);
482 res = ast_playstream(fs);
484 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
486 return RESULT_SHOWUSAGE;
488 return RESULT_FAILURE;
490 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
491 /* this is to check for if ast_waitstream closed the stream, we probably are at
492 * the end of the stream, return that amount, else check for the amount */
493 sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
494 ast_stopstream(chan);
496 /* Stop this command, don't print a result line, as there is a new command */
497 return RESULT_SUCCESS;
499 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
501 return RESULT_SUCCESS;
503 return RESULT_FAILURE;
506 /* get option - really similar to the handle_streamfile, but with a timeout */
507 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
510 struct ast_filestream *fs;
511 long sample_offset = 0;
514 char *edigits = NULL;
516 if ( argc < 4 || argc > 5 )
517 return RESULT_SHOWUSAGE;
523 timeout = atoi(argv[4]);
524 else if (chan->pbx->dtimeout) {
525 /* by default dtimeout is set to 5sec */
526 timeout = chan->pbx->dtimeout * 1000; /* in msec */
529 fs = ast_openstream(chan, argv[2], chan->language);
531 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
532 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
533 return RESULT_SUCCESS;
535 if (option_verbose > 2)
536 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
538 ast_seekstream(fs, 0, SEEK_END);
539 max_length = ast_tellstream(fs);
540 ast_seekstream(fs, sample_offset, SEEK_SET);
541 res = ast_applystream(chan, fs);
542 res = ast_playstream(fs);
544 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
546 return RESULT_SHOWUSAGE;
548 return RESULT_FAILURE;
550 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
551 /* this is to check for if ast_waitstream closed the stream, we probably are at
552 * the end of the stream, return that amount, else check for the amount */
553 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
554 ast_stopstream(chan);
556 /* Stop this command, don't print a result line, as there is a new command */
557 return RESULT_SUCCESS;
560 /* If the user didnt press a key, wait for digitTimeout*/
562 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
563 /* Make sure the new result is in the escape digits of the GET OPTION */
564 if ( !strchr(edigits,res) )
568 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
570 return RESULT_SUCCESS;
572 return RESULT_FAILURE;
578 /*--- handle_saynumber: Say number in various language syntaxes ---*/
579 /* Need to add option for gender here as well. Coders wanted */
580 /* While waiting, we're sending a (char *) NULL. */
581 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
586 return RESULT_SHOWUSAGE;
587 if (sscanf(argv[2], "%d", &num) != 1)
588 return RESULT_SHOWUSAGE;
589 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
591 return RESULT_SUCCESS;
592 fdprintf(agi->fd, "200 result=%d\n", res);
594 return RESULT_SUCCESS;
596 return RESULT_FAILURE;
599 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
605 return RESULT_SHOWUSAGE;
606 if (sscanf(argv[2], "%d", &num) != 1)
607 return RESULT_SHOWUSAGE;
609 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
610 if (res == 1) /* New command */
611 return RESULT_SUCCESS;
612 fdprintf(agi->fd, "200 result=%d\n", res);
614 return RESULT_SUCCESS;
616 return RESULT_FAILURE;
619 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
624 return RESULT_SHOWUSAGE;
626 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
627 if (res == 1) /* New command */
628 return RESULT_SUCCESS;
629 fdprintf(agi->fd, "200 result=%d\n", res);
631 return RESULT_SUCCESS;
633 return RESULT_FAILURE;
636 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
641 return RESULT_SHOWUSAGE;
642 if (sscanf(argv[2], "%d", &num) != 1)
643 return RESULT_SHOWUSAGE;
644 res = ast_say_date(chan, num, argv[3], chan->language);
646 return RESULT_SUCCESS;
647 fdprintf(agi->fd, "200 result=%d\n", res);
649 return RESULT_SUCCESS;
651 return RESULT_FAILURE;
654 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
659 return RESULT_SHOWUSAGE;
660 if (sscanf(argv[2], "%d", &num) != 1)
661 return RESULT_SHOWUSAGE;
662 res = ast_say_time(chan, num, argv[3], chan->language);
664 return RESULT_SUCCESS;
665 fdprintf(agi->fd, "200 result=%d\n", res);
667 return RESULT_SUCCESS;
669 return RESULT_FAILURE;
672 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
676 char *format, *zone=NULL;
679 return RESULT_SHOWUSAGE;
684 if (!strcasecmp(chan->language, "de")) {
685 format = "A dBY HMS";
687 format = "ABdY 'digits/at' IMp";
691 if (argc > 5 && !ast_strlen_zero(argv[5]))
694 if (sscanf(argv[2], "%ld", &unixtime) != 1)
695 return RESULT_SHOWUSAGE;
697 res = ast_say_date_with_format(chan, (time_t) unixtime, argv[3], chan->language, format, zone);
699 return RESULT_SUCCESS;
701 fdprintf(agi->fd, "200 result=%d\n", res);
704 return RESULT_SUCCESS;
706 return RESULT_FAILURE;
709 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
714 return RESULT_SHOWUSAGE;
716 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
717 if (res == 1) /* New command */
718 return RESULT_SUCCESS;
719 fdprintf(agi->fd, "200 result=%d\n", res);
721 return RESULT_SUCCESS;
723 return RESULT_FAILURE;
726 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
734 return RESULT_SHOWUSAGE;
736 timeout = atoi(argv[3]);
743 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
744 if (res == 2) /* New command */
745 return RESULT_SUCCESS;
747 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
749 fdprintf(agi->fd, "200 result=-1\n");
751 fdprintf(agi->fd, "200 result=%s\n", data);
752 return RESULT_SUCCESS;
755 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
759 return RESULT_SHOWUSAGE;
760 ast_copy_string(chan->context, argv[2], sizeof(chan->context));
761 fdprintf(agi->fd, "200 result=0\n");
762 return RESULT_SUCCESS;
765 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
768 return RESULT_SHOWUSAGE;
769 ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
770 fdprintf(agi->fd, "200 result=0\n");
771 return RESULT_SUCCESS;
774 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
778 return RESULT_SHOWUSAGE;
780 if (sscanf(argv[2], "%d", &pri) != 1) {
781 if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
782 return RESULT_SHOWUSAGE;
785 ast_explicit_goto(chan, NULL, NULL, pri);
786 fdprintf(agi->fd, "200 result=0\n");
787 return RESULT_SUCCESS;
790 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
792 struct ast_filestream *fs;
794 struct timeval tv, start;
795 long sample_offset = 0;
799 struct ast_dsp *sildet=NULL; /* silence detector dsp */
800 int totalsilence = 0;
802 int silence = 0; /* amount of silence to allow */
803 int gotsilence = 0; /* did we timeout for silence? */
804 char *silencestr=NULL;
808 /* XXX EAGI FIXME XXX */
811 return RESULT_SHOWUSAGE;
812 if (sscanf(argv[5], "%d", &ms) != 1)
813 return RESULT_SHOWUSAGE;
816 silencestr = strchr(argv[6],'s');
817 if ((argc > 7) && (!silencestr))
818 silencestr = strchr(argv[7],'s');
819 if ((argc > 8) && (!silencestr))
820 silencestr = strchr(argv[8],'s');
823 if (strlen(silencestr) > 2) {
824 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
828 silence = atoi(silencestr);
836 rfmt = chan->readformat;
837 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
839 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
842 sildet = ast_dsp_new();
844 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
847 ast_dsp_set_threshold(sildet, 256);
850 /* backward compatibility, if no offset given, arg[6] would have been
851 * caught below and taken to be a beep, else if it is a digit then it is a
853 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
854 res = ast_streamfile(chan, "beep", chan->language);
856 if ((argc > 7) && (!strchr(argv[7], '=')))
857 res = ast_streamfile(chan, "beep", chan->language);
860 res = ast_waitstream(chan, argv[4]);
862 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
865 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
867 ast_dsp_free(sildet);
868 return RESULT_FAILURE;
872 ast_applystream(chan,fs);
873 /* really should have checks */
874 ast_seekstream(fs, sample_offset, SEEK_SET);
877 gettimeofday(&start, NULL);
878 gettimeofday(&tv, NULL);
879 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
880 res = ast_waitfor(chan, -1);
883 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
885 ast_dsp_free(sildet);
886 return RESULT_FAILURE;
890 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
893 ast_dsp_free(sildet);
894 return RESULT_FAILURE;
896 switch(f->frametype) {
898 if (strchr(argv[4], f->subclass)) {
899 /* This is an interrupting chracter */
900 sample_offset = ast_tellstream(fs);
901 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
905 ast_dsp_free(sildet);
906 return RESULT_SUCCESS;
909 case AST_FRAME_VOICE:
910 ast_writestream(fs, f);
911 /* this is a safe place to check progress since we know that fs
912 * is valid after a write, and it will then have our current
914 sample_offset = ast_tellstream(fs);
917 ast_dsp_silence(sildet, f, &dspsilence);
919 totalsilence = dspsilence;
923 if (totalsilence > silence) {
924 /* Ended happily with silence */
933 gettimeofday(&tv, NULL);
939 ast_stream_rewind(fs, silence-1000);
941 sample_offset = ast_tellstream(fs);
943 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
946 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
949 res = ast_set_read_format(chan, rfmt);
951 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
952 ast_dsp_free(sildet);
954 return RESULT_SUCCESS;
957 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
962 return RESULT_SHOWUSAGE;
963 if (sscanf(argv[2], "%d", &timeout) != 1)
964 return RESULT_SHOWUSAGE;
968 chan->whentohangup = time(NULL) + timeout;
970 chan->whentohangup = 0;
971 fdprintf(agi->fd, "200 result=0\n");
972 return RESULT_SUCCESS;
975 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
977 struct ast_channel *c;
979 /* no argument: hangup the current channel */
980 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
981 fdprintf(agi->fd, "200 result=1\n");
982 return RESULT_SUCCESS;
983 } else if (argc == 2) {
984 /* one argument: look for info on the specified channel */
985 c = ast_get_channel_by_name_locked(argv[1]);
987 /* we have a matching channel */
988 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
989 fdprintf(agi->fd, "200 result=1\n");
990 ast_mutex_unlock(&c->lock);
991 return RESULT_SUCCESS;
993 /* if we get this far no channel name matched the argument given */
994 fdprintf(agi->fd, "200 result=-1\n");
995 return RESULT_SUCCESS;
997 return RESULT_SHOWUSAGE;
1001 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1004 struct ast_app *app;
1007 return RESULT_SHOWUSAGE;
1009 if (option_verbose > 2)
1010 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
1012 app = pbx_findapp(argv[1]);
1015 res = pbx_exec(chan, app, argv[2], 1);
1017 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
1020 fdprintf(agi->fd, "200 result=%d\n", res);
1025 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1028 char *l = NULL, *n = NULL;
1031 ast_copy_string(tmp, argv[2], sizeof(tmp));
1032 ast_callerid_parse(tmp, &n, &l);
1034 ast_shrink_phone_number(l);
1039 ast_set_callerid(chan, l, n, NULL);
1042 fdprintf(agi->fd, "200 result=1\n");
1043 return RESULT_SUCCESS;
1046 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1048 struct ast_channel *c;
1050 /* no argument: supply info on the current channel */
1051 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
1052 return RESULT_SUCCESS;
1053 } else if (argc == 3) {
1054 /* one argument: look for info on the specified channel */
1055 c = ast_get_channel_by_name_locked(argv[2]);
1057 fdprintf(agi->fd, "200 result=%d\n", c->_state);
1058 ast_mutex_unlock(&c->lock);
1059 return RESULT_SUCCESS;
1061 /* if we get this far no channel name matched the argument given */
1062 fdprintf(agi->fd, "200 result=-1\n");
1063 return RESULT_SUCCESS;
1065 return RESULT_SHOWUSAGE;
1069 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1072 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
1074 fdprintf(agi->fd, "200 result=1\n");
1075 return RESULT_SUCCESS;
1078 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1084 return RESULT_SHOWUSAGE;
1085 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
1087 fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
1089 fdprintf(agi->fd, "200 result=0\n");
1091 return RESULT_SUCCESS;
1094 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1097 struct ast_channel *chan2=NULL;
1099 if ((argc != 4) && (argc != 5))
1100 return RESULT_SHOWUSAGE;
1102 chan2 = ast_get_channel_by_name_locked(argv[4]);
1106 if (chan) { /* XXX isn't this chan2 ? */
1107 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
1108 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1110 fdprintf(agi->fd, "200 result=0\n");
1112 if (chan2 && (chan2 != chan))
1113 ast_mutex_unlock(&chan2->lock);
1114 return RESULT_SUCCESS;
1117 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1123 return RESULT_SHOWUSAGE;
1126 sscanf(argv[2], "%d", &level);
1130 prefix = VERBOSE_PREFIX_4;
1133 prefix = VERBOSE_PREFIX_3;
1136 prefix = VERBOSE_PREFIX_2;
1140 prefix = VERBOSE_PREFIX_1;
1144 if (level <= option_verbose)
1145 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
1147 fdprintf(agi->fd, "200 result=1\n");
1149 return RESULT_SUCCESS;
1152 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1158 return RESULT_SHOWUSAGE;
1159 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
1161 fdprintf(agi->fd, "200 result=0\n");
1163 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1165 return RESULT_SUCCESS;
1168 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1173 return RESULT_SHOWUSAGE;
1174 res = ast_db_put(argv[2], argv[3], argv[4]);
1176 fdprintf(agi->fd, "200 result=0\n");
1178 fdprintf(agi->fd, "200 result=1\n");
1180 return RESULT_SUCCESS;
1183 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1188 return RESULT_SHOWUSAGE;
1189 res = ast_db_del(argv[2], argv[3]);
1191 fdprintf(agi->fd, "200 result=0\n");
1193 fdprintf(agi->fd, "200 result=1\n");
1195 return RESULT_SUCCESS;
1198 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1201 if ((argc < 3) || (argc > 4))
1202 return RESULT_SHOWUSAGE;
1204 res = ast_db_deltree(argv[2], argv[3]);
1206 res = ast_db_deltree(argv[2], NULL);
1209 fdprintf(agi->fd, "200 result=0\n");
1211 fdprintf(agi->fd, "200 result=1\n");
1212 return RESULT_SUCCESS;
1215 static char debug_usage[] =
1216 "Usage: agi debug\n"
1217 " Enables dumping of AGI transactions for debugging purposes\n";
1219 static char no_debug_usage[] =
1220 "Usage: agi no debug\n"
1221 " Disables dumping of AGI transactions for debugging purposes\n";
1223 static int agi_do_debug(int fd, int argc, char *argv[])
1226 return RESULT_SHOWUSAGE;
1228 ast_cli(fd, "AGI Debugging Enabled\n");
1229 return RESULT_SUCCESS;
1232 static int agi_no_debug(int fd, int argc, char *argv[])
1235 return RESULT_SHOWUSAGE;
1237 ast_cli(fd, "AGI Debugging Disabled\n");
1238 return RESULT_SUCCESS;
1241 static struct ast_cli_entry cli_debug =
1242 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1244 static struct ast_cli_entry cli_no_debug =
1245 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1247 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1249 fdprintf(agi->fd, "200 result=0\n");
1250 return RESULT_SUCCESS;
1253 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1255 if (!strncasecmp(argv[2],"on",2)) {
1257 ast_moh_start(chan, argv[3]);
1259 ast_moh_start(chan, NULL);
1261 if (!strncasecmp(argv[2],"off",3)) {
1264 fdprintf(agi->fd, "200 result=0\n");
1265 return RESULT_SUCCESS;
1268 static char usage_setmusic[] =
1269 " Usage: SET MUSIC ON <on|off> <class>\n"
1270 " Enables/Disables the music on hold generator. If <class> is\n"
1271 " not specified, then the default music on hold class will be used.\n"
1272 " Always returns 0.\n";
1274 static char usage_dbput[] =
1275 " Usage: DATABASE PUT <family> <key> <value>\n"
1276 " Adds or updates an entry in the Asterisk database for a\n"
1277 " given family, key, and value.\n"
1278 " Returns 1 if successful, 0 otherwise.\n";
1280 static char usage_dbget[] =
1281 " Usage: DATABASE GET <family> <key>\n"
1282 " Retrieves an entry in the Asterisk database for a\n"
1283 " given family and key.\n"
1284 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1285 " is set and returns the variable in parentheses.\n"
1286 " Example return code: 200 result=1 (testvariable)\n";
1288 static char usage_dbdel[] =
1289 " Usage: DATABASE DEL <family> <key>\n"
1290 " Deletes an entry in the Asterisk database for a\n"
1291 " given family and key.\n"
1292 " Returns 1 if successful, 0 otherwise.\n";
1294 static char usage_dbdeltree[] =
1295 " Usage: DATABASE DELTREE <family> [keytree]\n"
1296 " Deletes a family or specific keytree within a family\n"
1297 " in the Asterisk database.\n"
1298 " Returns 1 if successful, 0 otherwise.\n";
1300 static char usage_verbose[] =
1301 " Usage: VERBOSE <message> <level>\n"
1302 " Sends <message> to the console via verbose message system.\n"
1303 " <level> is the the verbose level (1-4)\n"
1304 " Always returns 1.\n";
1306 static char usage_getvariable[] =
1307 " Usage: GET VARIABLE <variablename>\n"
1308 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1309 " is set and returns the variable in parentheses.\n"
1310 " example return code: 200 result=1 (testvariable)\n";
1312 static char usage_getvariablefull[] =
1313 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
1314 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
1315 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
1316 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
1317 " example return code: 200 result=1 (testvariable)\n";
1319 static char usage_setvariable[] =
1320 " Usage: SET VARIABLE <variablename> <value>\n";
1322 static char usage_channelstatus[] =
1323 " Usage: CHANNEL STATUS [<channelname>]\n"
1324 " Returns the status of the specified channel.\n"
1325 " If no channel name is given the returns the status of the\n"
1326 " current channel. Return values:\n"
1327 " 0 Channel is down and available\n"
1328 " 1 Channel is down, but reserved\n"
1329 " 2 Channel is off hook\n"
1330 " 3 Digits (or equivalent) have been dialed\n"
1331 " 4 Line is ringing\n"
1332 " 5 Remote end is ringing\n"
1334 " 7 Line is busy\n";
1336 static char usage_setcallerid[] =
1337 " Usage: SET CALLERID <number>\n"
1338 " Changes the callerid of the current channel.\n";
1340 static char usage_exec[] =
1341 " Usage: EXEC <application> <options>\n"
1342 " Executes <application> with given <options>.\n"
1343 " Returns whatever the application returns, or -2 on failure to find application\n";
1345 static char usage_hangup[] =
1346 " Usage: HANGUP [<channelname>]\n"
1347 " Hangs up the specified channel.\n"
1348 " If no channel name is given, hangs up the current channel\n";
1350 static char usage_answer[] =
1352 " Answers channel if not already in answer state. Returns -1 on\n"
1353 " channel failure, or 0 if successful.\n";
1355 static char usage_waitfordigit[] =
1356 " Usage: WAIT FOR DIGIT <timeout>\n"
1357 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1358 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1359 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1360 " for the timeout value if you desire the call to block indefinitely.\n";
1362 static char usage_sendtext[] =
1363 " Usage: SEND TEXT \"<text to send>\"\n"
1364 " Sends the given text on a channel. Most channels do not support the\n"
1365 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1366 " support text transmission. Returns -1 only on error/hangup. Text\n"
1367 " consisting of greater than one word should be placed in quotes since the\n"
1368 " command only accepts a single argument.\n";
1370 static char usage_recvchar[] =
1371 " Usage: RECEIVE CHAR <timeout>\n"
1372 " Receives a character of text on a channel. Specify timeout to be the\n"
1373 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1374 " do not support the reception of text. Returns the decimal value of the character\n"
1375 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1376 " -1 only on error/hangup.\n";
1378 static char usage_recvtext[] =
1379 " Usage: RECEIVE CHAR <timeout>\n"
1380 " Receives a string of text on a channel. Specify timeout to be the\n"
1381 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1382 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
1384 static char usage_tddmode[] =
1385 " Usage: TDD MODE <on|off>\n"
1386 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1387 " successful, or 0 if channel is not TDD-capable.\n";
1389 static char usage_sendimage[] =
1390 " Usage: SEND IMAGE <image>\n"
1391 " Sends the given image on a channel. Most channels do not support the\n"
1392 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1393 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1394 " should not include extensions.\n";
1396 static char usage_streamfile[] =
1397 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1398 " Send the given file, allowing playback to be interrupted by the given\n"
1399 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1400 " permitted. If sample offset is provided then the audio will seek to sample\n"
1401 " offset before play starts. Returns 0 if playback completes without a digit\n"
1402 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1403 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1404 " extension must not be included in the filename.\n";
1406 static char usage_getoption[] =
1407 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
1408 " Behaves similar to STREAM FILE but used with a timeout option.\n";
1410 static char usage_saynumber[] =
1411 " Usage: SAY NUMBER <number> <escape digits>\n"
1412 " Say a given number, returning early if any of the given DTMF digits\n"
1413 " are received on the channel. Returns 0 if playback completes without a digit\n"
1414 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1415 " -1 on error/hangup.\n";
1417 static char usage_saydigits[] =
1418 " Usage: SAY DIGITS <number> <escape digits>\n"
1419 " Say a given digit string, returning early if any of the given DTMF digits\n"
1420 " are received on the channel. Returns 0 if playback completes without a digit\n"
1421 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1422 " -1 on error/hangup.\n";
1424 static char usage_sayalpha[] =
1425 " Usage: SAY ALPHA <number> <escape digits>\n"
1426 " Say a given character string, returning early if any of the given DTMF digits\n"
1427 " are received on the channel. Returns 0 if playback completes without a digit\n"
1428 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1429 " -1 on error/hangup.\n";
1431 static char usage_saydate[] =
1432 " Usage: SAY DATE <date> <escape digits>\n"
1433 " Say a given date, returning early if any of the given DTMF digits are\n"
1434 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
1435 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1436 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1437 " digit if one was pressed or -1 on error/hangup.\n";
1439 static char usage_saytime[] =
1440 " Usage: SAY TIME <time> <escape digits>\n"
1441 " Say a given time, returning early if any of the given DTMF digits are\n"
1442 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1443 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1444 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1445 " digit if one was pressed or -1 on error/hangup.\n";
1447 static char usage_saydatetime[] =
1448 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
1449 " Say a given time, returning early if any of the given DTMF digits are\n"
1450 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1451 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
1452 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
1453 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
1454 " /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n"
1455 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1456 " digit if one was pressed or -1 on error/hangup.\n";
1458 static char usage_sayphonetic[] =
1459 " Usage: SAY PHONETIC <string> <escape digits>\n"
1460 " Say a given character string with phonetics, returning early if any of the\n"
1461 " given DTMF digits are received on the channel. Returns 0 if playback\n"
1462 " completes without a digit pressed, the ASCII numerical value of the digit\n"
1463 " if one was pressed, or -1 on error/hangup.\n";
1465 static char usage_getdata[] =
1466 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1467 " Stream the given file, and recieve DTMF data. Returns the digits received\n"
1468 "from the channel at the other end.\n";
1470 static char usage_setcontext[] =
1471 " Usage: SET CONTEXT <desired context>\n"
1472 " Sets the context for continuation upon exiting the application.\n";
1474 static char usage_setextension[] =
1475 " Usage: SET EXTENSION <new extension>\n"
1476 " Changes the extension for continuation upon exiting the application.\n";
1478 static char usage_setpriority[] =
1479 " Usage: SET PRIORITY <priority>\n"
1480 " Changes the priority for continuation upon exiting the application.\n"
1481 " The priority must be a valid priority or label.\n";
1483 static char usage_recordfile[] =
1484 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
1485 " [offset samples] [BEEP] [s=silence]\n"
1486 " Record to a file until a given dtmf digit in the sequence is received\n"
1487 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1488 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1489 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
1490 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
1491 " of seconds of silence allowed before the function returns despite the\n"
1492 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1493 " preceeded by \"s=\" and is also optional.\n";
1495 static char usage_autohangup[] =
1496 " Usage: SET AUTOHANGUP <time>\n"
1497 " Cause the channel to automatically hangup at <time> seconds in the\n"
1498 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
1499 " cause the autohangup feature to be disabled on this channel.\n";
1501 static char usage_noop[] =
1505 static agi_command commands[MAX_COMMANDS] = {
1506 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
1507 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1508 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1509 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1510 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1511 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1512 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1513 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
1514 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
1515 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
1516 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1517 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1518 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1519 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar },
1520 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext },
1521 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1522 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
1523 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1524 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1525 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1526 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate },
1527 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1528 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime },
1529 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1530 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1531 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1532 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1533 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1534 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1535 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
1536 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
1537 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1538 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1539 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
1540 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1541 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1544 static void join(char *s, size_t len, char *w[])
1548 /* Join words into a string */
1553 for (x=0; w[x]; x++) {
1555 strncat(s, " ", len - strlen(s) - 1);
1556 strncat(s, w[x], len - strlen(s) - 1);
1560 static int help_workhorse(int fd, char *match[])
1565 struct agi_command *e;
1567 join(matchstr, sizeof(matchstr), match);
1568 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1569 if (!commands[x].cmda[0]) break;
1572 join(fullcmd, sizeof(fullcmd), e->cmda);
1573 /* Hide commands that start with '_' */
1574 if (fullcmd[0] == '_')
1577 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1581 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1586 int agi_register(agi_command *agi)
1589 for (x=0; x<MAX_COMMANDS - 1; x++) {
1590 if (commands[x].cmda[0] == agi->cmda[0]) {
1591 ast_log(LOG_WARNING, "Command already registered!\n");
1595 for (x=0; x<MAX_COMMANDS - 1; x++) {
1596 if (!commands[x].cmda[0]) {
1601 ast_log(LOG_WARNING, "No more room for new commands!\n");
1605 void agi_unregister(agi_command *agi)
1608 for (x=0; x<MAX_COMMANDS - 1; x++) {
1609 if (commands[x].cmda[0] == agi->cmda[0]) {
1610 memset(&commands[x], 0, sizeof(agi_command));
1615 static agi_command *find_command(char *cmds[], int exact)
1621 for (x=0; x < sizeof(commands) / sizeof(commands[0]); x++) {
1622 if (!commands[x].cmda[0])
1624 /* start optimistic */
1626 for (y=0; match && cmds[y]; y++) {
1627 /* If there are no more words in the command (and we're looking for
1628 an exact match) or there is a difference between the two words,
1629 then this is not a match */
1630 if (!commands[x].cmda[y] && !exact)
1632 /* don't segfault if the next part of a command doesn't exist */
1633 if (!commands[x].cmda[y])
1635 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1638 /* If more words are needed to complete the command then this is not
1639 a candidate (unless we're looking for a really inexact answer */
1640 if ((exact > -1) && commands[x].cmda[y])
1643 return &commands[x];
1649 static int parse_args(char *s, int *max, char *argv[])
1661 /* If it's escaped, put a literal quote */
1666 if (quoted && whitespace) {
1667 /* If we're starting a quote, coming off white space start a new word, too */
1675 if (!quoted && !escaped) {
1676 /* If we're not quoted, mark this as whitespace, and
1677 end the previous argument */
1681 /* Otherwise, just treat it as anything else */
1685 /* If we're escaped, print a literal, otherwise enable escaping */
1695 if (x >= MAX_ARGS -1) {
1696 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1699 /* Coming off of whitespace, start the next argument */
1708 /* Null terminate */
1715 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1717 char *argv[MAX_ARGS];
1723 parse_args(buf, &argc, argv);
1726 for (x=0; x<argc; x++)
1727 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1729 c = find_command(argv, 0);
1731 res = c->handler(chan, agi, argc, argv);
1733 case RESULT_SHOWUSAGE:
1734 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1735 fdprintf(agi->fd, c->usage);
1736 fdprintf(agi->fd, "520 End of proper usage.\n");
1738 case AST_PBX_KEEPALIVE:
1739 /* We've been asked to keep alive, so do so */
1740 return AST_PBX_KEEPALIVE;
1742 case RESULT_FAILURE:
1743 /* They've already given the failure. We've been hung up on so handle this
1748 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1753 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1755 struct ast_channel *c;
1758 int returnstatus = 0;
1759 struct ast_frame *f;
1762 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1763 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1766 if (!(readf = fdopen(agi->ctrl, "r"))) {
1767 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1774 setup_env(chan, request, agi->fd, (agi->audio > -1));
1777 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1780 /* Idle the channel until we get a command */
1783 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1787 /* If it's voice, write it to the audio pipe */
1788 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1789 /* Write, ignoring errors */
1790 write(agi->audio, f->data, f->datalen);
1794 } else if (outfd > -1) {
1796 if (!fgets(buf, sizeof(buf), readf)) {
1797 /* Program terminated */
1800 if (option_verbose > 2)
1801 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1802 /* No need to kill the pid anymore, since they closed us */
1806 /* get rid of trailing newline, if any */
1807 if (*buf && buf[strlen(buf) - 1] == '\n')
1808 buf[strlen(buf) - 1] = 0;
1810 ast_verbose("AGI Rx << %s\n", buf);
1811 returnstatus |= agi_handle_command(chan, agi, buf);
1812 /* If the handle_command returns -1, we need to stop */
1813 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1818 ast_log(LOG_WARNING, "No channel, no fd?\n");
1824 /* Notify process */
1828 return returnstatus;
1831 static int handle_showagi(int fd, int argc, char *argv[]) {
1832 struct agi_command *e;
1835 return RESULT_SHOWUSAGE;
1837 e = find_command(argv + 2, 1);
1839 ast_cli(fd, e->usage);
1841 if (find_command(argv + 2, -1)) {
1842 return help_workhorse(fd, argv + 1);
1844 join(fullcmd, sizeof(fullcmd), argv+1);
1845 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1849 return help_workhorse(fd, NULL);
1851 return RESULT_SUCCESS;
1854 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1855 struct agi_command *e;
1862 return RESULT_SHOWUSAGE;
1864 if (!(htmlfile = fopen(argv[2], "wt"))) {
1865 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1866 return RESULT_SHOWUSAGE;
1869 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1870 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1873 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1875 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1877 if (!commands[x].cmda[0]) break;
1880 join(fullcmd, sizeof(fullcmd), e->cmda);
1881 /* Hide commands that start with '_' */
1882 if (fullcmd[0] == '_')
1885 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1886 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1890 tempstr = strsep(&stringp, "\n");
1892 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1894 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1895 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1896 fprintf(htmlfile, "%s<BR>\n",tempstr);
1899 fprintf(htmlfile, "</TD></TR>\n");
1900 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1904 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1906 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1907 return RESULT_SUCCESS;
1910 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1913 struct localuser *u;
1914 char *argv[MAX_ARGS];
1916 char *tmp = (char *)buf;
1924 if (!data || ast_strlen_zero(data)) {
1925 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1928 ast_copy_string(buf, data, sizeof(buf));
1930 memset(&agi, 0, sizeof(agi));
1931 while ((stringp = strsep(&tmp, "|"))) {
1932 argv[argc++] = stringp;
1938 /* Answer if need be */
1939 if (chan->_state != AST_STATE_UP) {
1940 if (ast_answer(chan)) {
1941 LOCAL_USER_REMOVE(u);
1946 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1951 res = run_agi(chan, argv[0], &agi, pid, dead);
1956 LOCAL_USER_REMOVE(u);
1960 static int agi_exec(struct ast_channel *chan, void *data)
1962 if (chan->_softhangup)
1963 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1964 return agi_exec_full(chan, data, 0, 0);
1967 static int eagi_exec(struct ast_channel *chan, void *data)
1972 if (chan->_softhangup)
1973 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1974 readformat = chan->readformat;
1975 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1976 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1979 res = agi_exec_full(chan, data, 1, 0);
1981 if (ast_set_read_format(chan, readformat)) {
1982 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1988 static int deadagi_exec(struct ast_channel *chan, void *data)
1990 return agi_exec_full(chan, data, 0, 1);
1993 static char showagi_help[] =
1994 "Usage: show agi [topic]\n"
1995 " When called with a topic as an argument, displays usage\n"
1996 " information on the given command. If called without a\n"
1997 " topic, it provides a list of AGI commands.\n";
2000 static char dumpagihtml_help[] =
2001 "Usage: dump agihtml <filename>\n"
2002 " Dumps the agi command list in html format to given filename\n";
2004 static struct ast_cli_entry showagi =
2005 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
2007 static struct ast_cli_entry dumpagihtml =
2008 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
2010 int unload_module(void)
2012 STANDARD_HANGUP_LOCALUSERS;
2013 ast_cli_unregister(&showagi);
2014 ast_cli_unregister(&dumpagihtml);
2015 ast_cli_unregister(&cli_debug);
2016 ast_cli_unregister(&cli_no_debug);
2017 ast_unregister_application(eapp);
2018 ast_unregister_application(deadapp);
2019 return ast_unregister_application(app);
2022 int load_module(void)
2024 ast_cli_register(&showagi);
2025 ast_cli_register(&dumpagihtml);
2026 ast_cli_register(&cli_debug);
2027 ast_cli_register(&cli_no_debug);
2028 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
2029 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
2030 return ast_register_application(app, agi_exec, synopsis, descrip);
2033 char *description(void)
2041 STANDARD_USECOUNT(res);
2047 return ASTERISK_GPL_KEY;