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);
417 fdprintf(agi->fd, "200 result=-1\n");
419 return RESULT_SUCCESS;
422 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
426 return RESULT_SHOWUSAGE;
427 if (!strncasecmp(argv[2],"on",2))
431 if (!strncasecmp(argv[2],"mate",4))
433 if (!strncasecmp(argv[2],"tdd",3))
435 res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
436 if(res != RESULT_SUCCESS)
437 fdprintf(agi->fd, "200 result=0\n");
439 fdprintf(agi->fd, "200 result=1\n");
440 return RESULT_SUCCESS;
443 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
447 return RESULT_SHOWUSAGE;
448 res = ast_send_image(chan, argv[2]);
449 if (!ast_check_hangup(chan))
451 fdprintf(agi->fd, "200 result=%d\n", res);
453 return RESULT_SUCCESS;
455 return RESULT_FAILURE;
458 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
461 struct ast_filestream *fs;
462 long sample_offset = 0;
466 return RESULT_SHOWUSAGE;
468 return RESULT_SHOWUSAGE;
469 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
470 return RESULT_SHOWUSAGE;
472 fs = ast_openstream(chan, argv[2], chan->language);
474 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
475 return RESULT_SUCCESS;
477 ast_seekstream(fs, 0, SEEK_END);
478 max_length = ast_tellstream(fs);
479 ast_seekstream(fs, sample_offset, SEEK_SET);
480 res = ast_applystream(chan, fs);
481 res = ast_playstream(fs);
483 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
485 return RESULT_SHOWUSAGE;
487 return RESULT_FAILURE;
489 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
490 /* this is to check for if ast_waitstream closed the stream, we probably are at
491 * the end of the stream, return that amount, else check for the amount */
492 sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
493 ast_stopstream(chan);
495 /* Stop this command, don't print a result line, as there is a new command */
496 return RESULT_SUCCESS;
498 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
500 return RESULT_SUCCESS;
502 return RESULT_FAILURE;
505 /* get option - really similar to the handle_streamfile, but with a timeout */
506 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
509 struct ast_filestream *fs;
510 long sample_offset = 0;
513 char *edigits = NULL;
515 if ( argc < 4 || argc > 5 )
516 return RESULT_SHOWUSAGE;
522 timeout = atoi(argv[4]);
523 else if (chan->pbx->dtimeout) {
524 /* by default dtimeout is set to 5sec */
525 timeout = chan->pbx->dtimeout * 1000; /* in msec */
528 fs = ast_openstream(chan, argv[2], chan->language);
530 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
531 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
532 return RESULT_SUCCESS;
534 if (option_verbose > 2)
535 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
537 ast_seekstream(fs, 0, SEEK_END);
538 max_length = ast_tellstream(fs);
539 ast_seekstream(fs, sample_offset, SEEK_SET);
540 res = ast_applystream(chan, fs);
541 res = ast_playstream(fs);
543 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
545 return RESULT_SHOWUSAGE;
547 return RESULT_FAILURE;
549 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
550 /* this is to check for if ast_waitstream closed the stream, we probably are at
551 * the end of the stream, return that amount, else check for the amount */
552 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
553 ast_stopstream(chan);
555 /* Stop this command, don't print a result line, as there is a new command */
556 return RESULT_SUCCESS;
559 /* If the user didnt press a key, wait for digitTimeout*/
561 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
562 /* Make sure the new result is in the escape digits of the GET OPTION */
563 if ( !strchr(edigits,res) )
567 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
569 return RESULT_SUCCESS;
571 return RESULT_FAILURE;
577 /*--- handle_saynumber: Say number in various language syntaxes ---*/
578 /* Need to add option for gender here as well. Coders wanted */
579 /* While waiting, we're sending a (char *) NULL. */
580 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
585 return RESULT_SHOWUSAGE;
586 if (sscanf(argv[2], "%d", &num) != 1)
587 return RESULT_SHOWUSAGE;
588 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
590 return RESULT_SUCCESS;
591 fdprintf(agi->fd, "200 result=%d\n", res);
593 return RESULT_SUCCESS;
595 return RESULT_FAILURE;
598 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
604 return RESULT_SHOWUSAGE;
605 if (sscanf(argv[2], "%d", &num) != 1)
606 return RESULT_SHOWUSAGE;
608 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
609 if (res == 1) /* New command */
610 return RESULT_SUCCESS;
611 fdprintf(agi->fd, "200 result=%d\n", res);
613 return RESULT_SUCCESS;
615 return RESULT_FAILURE;
618 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
623 return RESULT_SHOWUSAGE;
625 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
626 if (res == 1) /* New command */
627 return RESULT_SUCCESS;
628 fdprintf(agi->fd, "200 result=%d\n", res);
630 return RESULT_SUCCESS;
632 return RESULT_FAILURE;
635 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
640 return RESULT_SHOWUSAGE;
641 if (sscanf(argv[2], "%d", &num) != 1)
642 return RESULT_SHOWUSAGE;
643 res = ast_say_date(chan, num, argv[3], chan->language);
645 return RESULT_SUCCESS;
646 fdprintf(agi->fd, "200 result=%d\n", res);
648 return RESULT_SUCCESS;
650 return RESULT_FAILURE;
653 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
658 return RESULT_SHOWUSAGE;
659 if (sscanf(argv[2], "%d", &num) != 1)
660 return RESULT_SHOWUSAGE;
661 res = ast_say_time(chan, num, argv[3], chan->language);
663 return RESULT_SUCCESS;
664 fdprintf(agi->fd, "200 result=%d\n", res);
666 return RESULT_SUCCESS;
668 return RESULT_FAILURE;
671 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
675 char *format, *zone=NULL;
678 return RESULT_SHOWUSAGE;
683 if (!strcasecmp(chan->language, "de")) {
684 format = "A dBY HMS";
686 format = "ABdY 'digits/at' IMp";
690 if (argc > 5 && !ast_strlen_zero(argv[5]))
693 if (sscanf(argv[2], "%ld", &unixtime) != 1)
694 return RESULT_SHOWUSAGE;
696 res = ast_say_date_with_format(chan, (time_t) unixtime, argv[3], chan->language, format, zone);
698 return RESULT_SUCCESS;
700 fdprintf(agi->fd, "200 result=%d\n", res);
703 return RESULT_SUCCESS;
705 return RESULT_FAILURE;
708 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
713 return RESULT_SHOWUSAGE;
715 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
716 if (res == 1) /* New command */
717 return RESULT_SUCCESS;
718 fdprintf(agi->fd, "200 result=%d\n", res);
720 return RESULT_SUCCESS;
722 return RESULT_FAILURE;
725 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
733 return RESULT_SHOWUSAGE;
735 timeout = atoi(argv[3]);
742 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
743 if (res == 2) /* New command */
744 return RESULT_SUCCESS;
746 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
748 fdprintf(agi->fd, "200 result=-1\n");
750 fdprintf(agi->fd, "200 result=%s\n", data);
751 return RESULT_SUCCESS;
754 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
758 return RESULT_SHOWUSAGE;
759 ast_copy_string(chan->context, argv[2], sizeof(chan->context));
760 fdprintf(agi->fd, "200 result=0\n");
761 return RESULT_SUCCESS;
764 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
767 return RESULT_SHOWUSAGE;
768 ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
769 fdprintf(agi->fd, "200 result=0\n");
770 return RESULT_SUCCESS;
773 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
777 return RESULT_SHOWUSAGE;
779 if (sscanf(argv[2], "%d", &pri) != 1) {
780 if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
781 return RESULT_SHOWUSAGE;
784 ast_explicit_goto(chan, NULL, NULL, pri);
785 fdprintf(agi->fd, "200 result=0\n");
786 return RESULT_SUCCESS;
789 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
791 struct ast_filestream *fs;
793 struct timeval tv, start;
794 long sample_offset = 0;
798 struct ast_dsp *sildet=NULL; /* silence detector dsp */
799 int totalsilence = 0;
801 int silence = 0; /* amount of silence to allow */
802 int gotsilence = 0; /* did we timeout for silence? */
803 char *silencestr=NULL;
807 /* XXX EAGI FIXME XXX */
810 return RESULT_SHOWUSAGE;
811 if (sscanf(argv[5], "%d", &ms) != 1)
812 return RESULT_SHOWUSAGE;
815 silencestr = strchr(argv[6],'s');
816 if ((argc > 7) && (!silencestr))
817 silencestr = strchr(argv[7],'s');
818 if ((argc > 8) && (!silencestr))
819 silencestr = strchr(argv[8],'s');
822 if (strlen(silencestr) > 2) {
823 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
827 silence = atoi(silencestr);
835 rfmt = chan->readformat;
836 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
838 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
841 sildet = ast_dsp_new();
843 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
846 ast_dsp_set_threshold(sildet, 256);
849 /* backward compatibility, if no offset given, arg[6] would have been
850 * caught below and taken to be a beep, else if it is a digit then it is a
852 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
853 res = ast_streamfile(chan, "beep", chan->language);
855 if ((argc > 7) && (!strchr(argv[7], '=')))
856 res = ast_streamfile(chan, "beep", chan->language);
859 res = ast_waitstream(chan, argv[4]);
861 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
864 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
866 ast_dsp_free(sildet);
867 return RESULT_FAILURE;
871 ast_applystream(chan,fs);
872 /* really should have checks */
873 ast_seekstream(fs, sample_offset, SEEK_SET);
876 gettimeofday(&start, NULL);
877 gettimeofday(&tv, NULL);
878 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
879 res = ast_waitfor(chan, -1);
882 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
884 ast_dsp_free(sildet);
885 return RESULT_FAILURE;
889 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
892 ast_dsp_free(sildet);
893 return RESULT_FAILURE;
895 switch(f->frametype) {
897 if (strchr(argv[4], f->subclass)) {
898 /* This is an interrupting chracter */
899 sample_offset = ast_tellstream(fs);
900 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
904 ast_dsp_free(sildet);
905 return RESULT_SUCCESS;
908 case AST_FRAME_VOICE:
909 ast_writestream(fs, f);
910 /* this is a safe place to check progress since we know that fs
911 * is valid after a write, and it will then have our current
913 sample_offset = ast_tellstream(fs);
916 ast_dsp_silence(sildet, f, &dspsilence);
918 totalsilence = dspsilence;
922 if (totalsilence > silence) {
923 /* Ended happily with silence */
932 gettimeofday(&tv, NULL);
938 ast_stream_rewind(fs, silence-1000);
940 sample_offset = ast_tellstream(fs);
942 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
945 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
948 res = ast_set_read_format(chan, rfmt);
950 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
951 ast_dsp_free(sildet);
953 return RESULT_SUCCESS;
956 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
961 return RESULT_SHOWUSAGE;
962 if (sscanf(argv[2], "%d", &timeout) != 1)
963 return RESULT_SHOWUSAGE;
967 chan->whentohangup = time(NULL) + timeout;
969 chan->whentohangup = 0;
970 fdprintf(agi->fd, "200 result=0\n");
971 return RESULT_SUCCESS;
974 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
976 struct ast_channel *c;
978 /* no argument: hangup the current channel */
979 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
980 fdprintf(agi->fd, "200 result=1\n");
981 return RESULT_SUCCESS;
982 } else if (argc == 2) {
983 /* one argument: look for info on the specified channel */
984 c = ast_get_channel_by_name_locked(argv[1]);
986 /* we have a matching channel */
987 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
988 fdprintf(agi->fd, "200 result=1\n");
989 ast_mutex_unlock(&c->lock);
990 return RESULT_SUCCESS;
992 /* if we get this far no channel name matched the argument given */
993 fdprintf(agi->fd, "200 result=-1\n");
994 return RESULT_SUCCESS;
996 return RESULT_SHOWUSAGE;
1000 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1003 struct ast_app *app;
1006 return RESULT_SHOWUSAGE;
1008 if (option_verbose > 2)
1009 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
1011 app = pbx_findapp(argv[1]);
1014 res = pbx_exec(chan, app, argv[2], 1);
1016 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
1019 fdprintf(agi->fd, "200 result=%d\n", res);
1024 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1027 char *l = NULL, *n = NULL;
1030 ast_copy_string(tmp, argv[2], sizeof(tmp));
1031 ast_callerid_parse(tmp, &n, &l);
1033 ast_shrink_phone_number(l);
1038 ast_set_callerid(chan, l, n, NULL);
1041 fdprintf(agi->fd, "200 result=1\n");
1042 return RESULT_SUCCESS;
1045 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1047 struct ast_channel *c;
1049 /* no argument: supply info on the current channel */
1050 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
1051 return RESULT_SUCCESS;
1052 } else if (argc == 3) {
1053 /* one argument: look for info on the specified channel */
1054 c = ast_get_channel_by_name_locked(argv[2]);
1056 fdprintf(agi->fd, "200 result=%d\n", c->_state);
1057 ast_mutex_unlock(&c->lock);
1058 return RESULT_SUCCESS;
1060 /* if we get this far no channel name matched the argument given */
1061 fdprintf(agi->fd, "200 result=-1\n");
1062 return RESULT_SUCCESS;
1064 return RESULT_SHOWUSAGE;
1068 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1071 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
1073 fdprintf(agi->fd, "200 result=1\n");
1074 return RESULT_SUCCESS;
1077 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1083 return RESULT_SHOWUSAGE;
1084 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
1086 fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
1088 fdprintf(agi->fd, "200 result=0\n");
1090 return RESULT_SUCCESS;
1093 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1096 struct ast_channel *chan2=NULL;
1098 if ((argc != 4) && (argc != 5))
1099 return RESULT_SHOWUSAGE;
1101 chan2 = ast_get_channel_by_name_locked(argv[4]);
1105 if (chan) { /* XXX isn't this chan2 ? */
1106 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
1107 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1109 fdprintf(agi->fd, "200 result=0\n");
1111 if (chan2 && (chan2 != chan))
1112 ast_mutex_unlock(&chan2->lock);
1113 return RESULT_SUCCESS;
1116 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1122 return RESULT_SHOWUSAGE;
1125 sscanf(argv[2], "%d", &level);
1129 prefix = VERBOSE_PREFIX_4;
1132 prefix = VERBOSE_PREFIX_3;
1135 prefix = VERBOSE_PREFIX_2;
1139 prefix = VERBOSE_PREFIX_1;
1143 if (level <= option_verbose)
1144 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
1146 fdprintf(agi->fd, "200 result=1\n");
1148 return RESULT_SUCCESS;
1151 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1157 return RESULT_SHOWUSAGE;
1158 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
1160 fdprintf(agi->fd, "200 result=0\n");
1162 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1164 return RESULT_SUCCESS;
1167 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1172 return RESULT_SHOWUSAGE;
1173 res = ast_db_put(argv[2], argv[3], argv[4]);
1175 fdprintf(agi->fd, "200 result=0\n");
1177 fdprintf(agi->fd, "200 result=1\n");
1179 return RESULT_SUCCESS;
1182 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1187 return RESULT_SHOWUSAGE;
1188 res = ast_db_del(argv[2], argv[3]);
1190 fdprintf(agi->fd, "200 result=0\n");
1192 fdprintf(agi->fd, "200 result=1\n");
1194 return RESULT_SUCCESS;
1197 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1200 if ((argc < 3) || (argc > 4))
1201 return RESULT_SHOWUSAGE;
1203 res = ast_db_deltree(argv[2], argv[3]);
1205 res = ast_db_deltree(argv[2], NULL);
1208 fdprintf(agi->fd, "200 result=0\n");
1210 fdprintf(agi->fd, "200 result=1\n");
1211 return RESULT_SUCCESS;
1214 static char debug_usage[] =
1215 "Usage: agi debug\n"
1216 " Enables dumping of AGI transactions for debugging purposes\n";
1218 static char no_debug_usage[] =
1219 "Usage: agi no debug\n"
1220 " Disables dumping of AGI transactions for debugging purposes\n";
1222 static int agi_do_debug(int fd, int argc, char *argv[])
1225 return RESULT_SHOWUSAGE;
1227 ast_cli(fd, "AGI Debugging Enabled\n");
1228 return RESULT_SUCCESS;
1231 static int agi_no_debug(int fd, int argc, char *argv[])
1234 return RESULT_SHOWUSAGE;
1236 ast_cli(fd, "AGI Debugging Disabled\n");
1237 return RESULT_SUCCESS;
1240 static struct ast_cli_entry cli_debug =
1241 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1243 static struct ast_cli_entry cli_no_debug =
1244 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1246 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1248 fdprintf(agi->fd, "200 result=0\n");
1249 return RESULT_SUCCESS;
1252 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1254 if (!strncasecmp(argv[2],"on",2)) {
1256 ast_moh_start(chan, argv[3]);
1258 ast_moh_start(chan, NULL);
1260 if (!strncasecmp(argv[2],"off",3)) {
1263 fdprintf(agi->fd, "200 result=0\n");
1264 return RESULT_SUCCESS;
1267 static char usage_setmusic[] =
1268 " Usage: SET MUSIC ON <on|off> <class>\n"
1269 " Enables/Disables the music on hold generator. If <class> is\n"
1270 " not specified, then the default music on hold class will be used.\n"
1271 " Always returns 0.\n";
1273 static char usage_dbput[] =
1274 " Usage: DATABASE PUT <family> <key> <value>\n"
1275 " Adds or updates an entry in the Asterisk database for a\n"
1276 " given family, key, and value.\n"
1277 " Returns 1 if successful, 0 otherwise.\n";
1279 static char usage_dbget[] =
1280 " Usage: DATABASE GET <family> <key>\n"
1281 " Retrieves an entry in the Asterisk database for a\n"
1282 " given family and key.\n"
1283 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1284 " is set and returns the variable in parentheses.\n"
1285 " Example return code: 200 result=1 (testvariable)\n";
1287 static char usage_dbdel[] =
1288 " Usage: DATABASE DEL <family> <key>\n"
1289 " Deletes an entry in the Asterisk database for a\n"
1290 " given family and key.\n"
1291 " Returns 1 if successful, 0 otherwise.\n";
1293 static char usage_dbdeltree[] =
1294 " Usage: DATABASE DELTREE <family> [keytree]\n"
1295 " Deletes a family or specific keytree within a family\n"
1296 " in the Asterisk database.\n"
1297 " Returns 1 if successful, 0 otherwise.\n";
1299 static char usage_verbose[] =
1300 " Usage: VERBOSE <message> <level>\n"
1301 " Sends <message> to the console via verbose message system.\n"
1302 " <level> is the the verbose level (1-4)\n"
1303 " Always returns 1.\n";
1305 static char usage_getvariable[] =
1306 " Usage: GET VARIABLE <variablename>\n"
1307 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1308 " is set and returns the variable in parentheses.\n"
1309 " example return code: 200 result=1 (testvariable)\n";
1311 static char usage_getvariablefull[] =
1312 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
1313 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
1314 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
1315 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
1316 " example return code: 200 result=1 (testvariable)\n";
1318 static char usage_setvariable[] =
1319 " Usage: SET VARIABLE <variablename> <value>\n";
1321 static char usage_channelstatus[] =
1322 " Usage: CHANNEL STATUS [<channelname>]\n"
1323 " Returns the status of the specified channel.\n"
1324 " If no channel name is given the returns the status of the\n"
1325 " current channel. Return values:\n"
1326 " 0 Channel is down and available\n"
1327 " 1 Channel is down, but reserved\n"
1328 " 2 Channel is off hook\n"
1329 " 3 Digits (or equivalent) have been dialed\n"
1330 " 4 Line is ringing\n"
1331 " 5 Remote end is ringing\n"
1333 " 7 Line is busy\n";
1335 static char usage_setcallerid[] =
1336 " Usage: SET CALLERID <number>\n"
1337 " Changes the callerid of the current channel.\n";
1339 static char usage_exec[] =
1340 " Usage: EXEC <application> <options>\n"
1341 " Executes <application> with given <options>.\n"
1342 " Returns whatever the application returns, or -2 on failure to find application\n";
1344 static char usage_hangup[] =
1345 " Usage: HANGUP [<channelname>]\n"
1346 " Hangs up the specified channel.\n"
1347 " If no channel name is given, hangs up the current channel\n";
1349 static char usage_answer[] =
1351 " Answers channel if not already in answer state. Returns -1 on\n"
1352 " channel failure, or 0 if successful.\n";
1354 static char usage_waitfordigit[] =
1355 " Usage: WAIT FOR DIGIT <timeout>\n"
1356 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1357 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1358 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1359 " for the timeout value if you desire the call to block indefinitely.\n";
1361 static char usage_sendtext[] =
1362 " Usage: SEND TEXT \"<text to send>\"\n"
1363 " Sends the given text on a channel. Most channels do not support the\n"
1364 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1365 " support text transmission. Returns -1 only on error/hangup. Text\n"
1366 " consisting of greater than one word should be placed in quotes since the\n"
1367 " command only accepts a single argument.\n";
1369 static char usage_recvchar[] =
1370 " Usage: RECEIVE CHAR <timeout>\n"
1371 " Receives a character of text on a channel. Specify timeout to be the\n"
1372 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1373 " do not support the reception of text. Returns the decimal value of the character\n"
1374 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1375 " -1 only on error/hangup.\n";
1377 static char usage_recvtext[] =
1378 " Usage: RECEIVE CHAR <timeout>\n"
1379 " Receives a string of text on a channel. Specify timeout to be the\n"
1380 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1381 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
1383 static char usage_tddmode[] =
1384 " Usage: TDD MODE <on|off>\n"
1385 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1386 " successful, or 0 if channel is not TDD-capable.\n";
1388 static char usage_sendimage[] =
1389 " Usage: SEND IMAGE <image>\n"
1390 " Sends the given image on a channel. Most channels do not support the\n"
1391 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1392 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1393 " should not include extensions.\n";
1395 static char usage_streamfile[] =
1396 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1397 " Send the given file, allowing playback to be interrupted by the given\n"
1398 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1399 " permitted. If sample offset is provided then the audio will seek to sample\n"
1400 " offset before play starts. Returns 0 if playback completes without a digit\n"
1401 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1402 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1403 " extension must not be included in the filename.\n";
1405 static char usage_getoption[] =
1406 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
1407 " Behaves similar to STREAM FILE but used with a timeout option.\n";
1409 static char usage_saynumber[] =
1410 " Usage: SAY NUMBER <number> <escape digits>\n"
1411 " Say a given number, returning early if any of the given DTMF digits\n"
1412 " are received on the channel. Returns 0 if playback completes without a digit\n"
1413 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1414 " -1 on error/hangup.\n";
1416 static char usage_saydigits[] =
1417 " Usage: SAY DIGITS <number> <escape digits>\n"
1418 " Say a given digit string, returning early if any of the given DTMF digits\n"
1419 " are received on the channel. Returns 0 if playback completes without a digit\n"
1420 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1421 " -1 on error/hangup.\n";
1423 static char usage_sayalpha[] =
1424 " Usage: SAY ALPHA <number> <escape digits>\n"
1425 " Say a given character string, returning early if any of the given DTMF digits\n"
1426 " are received on the channel. Returns 0 if playback completes without a digit\n"
1427 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1428 " -1 on error/hangup.\n";
1430 static char usage_saydate[] =
1431 " Usage: SAY DATE <date> <escape digits>\n"
1432 " Say a given date, returning early if any of the given DTMF digits are\n"
1433 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
1434 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1435 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1436 " digit if one was pressed or -1 on error/hangup.\n";
1438 static char usage_saytime[] =
1439 " Usage: SAY TIME <time> <escape digits>\n"
1440 " Say a given time, returning early if any of the given DTMF digits are\n"
1441 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1442 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1443 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1444 " digit if one was pressed or -1 on error/hangup.\n";
1446 static char usage_saydatetime[] =
1447 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
1448 " Say a given time, returning early if any of the given DTMF digits are\n"
1449 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1450 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
1451 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
1452 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
1453 " /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n"
1454 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1455 " digit if one was pressed or -1 on error/hangup.\n";
1457 static char usage_sayphonetic[] =
1458 " Usage: SAY PHONETIC <string> <escape digits>\n"
1459 " Say a given character string with phonetics, returning early if any of the\n"
1460 " given DTMF digits are received on the channel. Returns 0 if playback\n"
1461 " completes without a digit pressed, the ASCII numerical value of the digit\n"
1462 " if one was pressed, or -1 on error/hangup.\n";
1464 static char usage_getdata[] =
1465 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1466 " Stream the given file, and recieve DTMF data. Returns the digits received\n"
1467 "from the channel at the other end.\n";
1469 static char usage_setcontext[] =
1470 " Usage: SET CONTEXT <desired context>\n"
1471 " Sets the context for continuation upon exiting the application.\n";
1473 static char usage_setextension[] =
1474 " Usage: SET EXTENSION <new extension>\n"
1475 " Changes the extension for continuation upon exiting the application.\n";
1477 static char usage_setpriority[] =
1478 " Usage: SET PRIORITY <priority>\n"
1479 " Changes the priority for continuation upon exiting the application.\n"
1480 " The priority must be a valid priority or label.\n";
1482 static char usage_recordfile[] =
1483 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
1484 " [offset samples] [BEEP] [s=silence]\n"
1485 " Record to a file until a given dtmf digit in the sequence is received\n"
1486 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1487 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1488 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
1489 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
1490 " of seconds of silence allowed before the function returns despite the\n"
1491 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1492 " preceeded by \"s=\" and is also optional.\n";
1494 static char usage_autohangup[] =
1495 " Usage: SET AUTOHANGUP <time>\n"
1496 " Cause the channel to automatically hangup at <time> seconds in the\n"
1497 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
1498 " cause the autohangup feature to be disabled on this channel.\n";
1500 static char usage_noop[] =
1504 static agi_command commands[MAX_COMMANDS] = {
1505 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
1506 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1507 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1508 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1509 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1510 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1511 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1512 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
1513 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
1514 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
1515 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1516 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1517 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1518 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar },
1519 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext },
1520 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1521 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
1522 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1523 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1524 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1525 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate },
1526 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1527 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime },
1528 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1529 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1530 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1531 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1532 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1533 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1534 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
1535 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
1536 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1537 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1538 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
1539 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1540 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1543 static void join(char *s, size_t len, char *w[])
1547 /* Join words into a string */
1552 for (x=0; w[x]; x++) {
1554 strncat(s, " ", len - strlen(s) - 1);
1555 strncat(s, w[x], len - strlen(s) - 1);
1559 static int help_workhorse(int fd, char *match[])
1564 struct agi_command *e;
1566 join(matchstr, sizeof(matchstr), match);
1567 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1568 if (!commands[x].cmda[0]) break;
1571 join(fullcmd, sizeof(fullcmd), e->cmda);
1572 /* Hide commands that start with '_' */
1573 if (fullcmd[0] == '_')
1576 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1580 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1585 int agi_register(agi_command *agi)
1588 for (x=0; x<MAX_COMMANDS - 1; x++) {
1589 if (commands[x].cmda[0] == agi->cmda[0]) {
1590 ast_log(LOG_WARNING, "Command already registered!\n");
1594 for (x=0; x<MAX_COMMANDS - 1; x++) {
1595 if (!commands[x].cmda[0]) {
1600 ast_log(LOG_WARNING, "No more room for new commands!\n");
1604 void agi_unregister(agi_command *agi)
1607 for (x=0; x<MAX_COMMANDS - 1; x++) {
1608 if (commands[x].cmda[0] == agi->cmda[0]) {
1609 memset(&commands[x], 0, sizeof(agi_command));
1614 static agi_command *find_command(char *cmds[], int exact)
1620 for (x=0; x < sizeof(commands) / sizeof(commands[0]); x++) {
1621 if (!commands[x].cmda[0])
1623 /* start optimistic */
1625 for (y=0; match && cmds[y]; y++) {
1626 /* If there are no more words in the command (and we're looking for
1627 an exact match) or there is a difference between the two words,
1628 then this is not a match */
1629 if (!commands[x].cmda[y] && !exact)
1631 /* don't segfault if the next part of a command doesn't exist */
1632 if (!commands[x].cmda[y])
1634 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1637 /* If more words are needed to complete the command then this is not
1638 a candidate (unless we're looking for a really inexact answer */
1639 if ((exact > -1) && commands[x].cmda[y])
1642 return &commands[x];
1648 static int parse_args(char *s, int *max, char *argv[])
1660 /* If it's escaped, put a literal quote */
1665 if (quoted && whitespace) {
1666 /* If we're starting a quote, coming off white space start a new word, too */
1674 if (!quoted && !escaped) {
1675 /* If we're not quoted, mark this as whitespace, and
1676 end the previous argument */
1680 /* Otherwise, just treat it as anything else */
1684 /* If we're escaped, print a literal, otherwise enable escaping */
1694 if (x >= MAX_ARGS -1) {
1695 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1698 /* Coming off of whitespace, start the next argument */
1707 /* Null terminate */
1714 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1716 char *argv[MAX_ARGS];
1722 parse_args(buf, &argc, argv);
1725 for (x=0; x<argc; x++)
1726 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1728 c = find_command(argv, 0);
1730 res = c->handler(chan, agi, argc, argv);
1732 case RESULT_SHOWUSAGE:
1733 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1734 fdprintf(agi->fd, c->usage);
1735 fdprintf(agi->fd, "520 End of proper usage.\n");
1737 case AST_PBX_KEEPALIVE:
1738 /* We've been asked to keep alive, so do so */
1739 return AST_PBX_KEEPALIVE;
1741 case RESULT_FAILURE:
1742 /* They've already given the failure. We've been hung up on so handle this
1747 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1752 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1754 struct ast_channel *c;
1757 int returnstatus = 0;
1758 struct ast_frame *f;
1761 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1762 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1765 if (!(readf = fdopen(agi->ctrl, "r"))) {
1766 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1773 setup_env(chan, request, agi->fd, (agi->audio > -1));
1776 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1779 /* Idle the channel until we get a command */
1782 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1786 /* If it's voice, write it to the audio pipe */
1787 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1788 /* Write, ignoring errors */
1789 write(agi->audio, f->data, f->datalen);
1793 } else if (outfd > -1) {
1795 if (!fgets(buf, sizeof(buf), readf)) {
1796 /* Program terminated */
1799 if (option_verbose > 2)
1800 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1801 /* No need to kill the pid anymore, since they closed us */
1805 /* get rid of trailing newline, if any */
1806 if (*buf && buf[strlen(buf) - 1] == '\n')
1807 buf[strlen(buf) - 1] = 0;
1809 ast_verbose("AGI Rx << %s\n", buf);
1810 returnstatus |= agi_handle_command(chan, agi, buf);
1811 /* If the handle_command returns -1, we need to stop */
1812 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1817 ast_log(LOG_WARNING, "No channel, no fd?\n");
1823 /* Notify process */
1827 return returnstatus;
1830 static int handle_showagi(int fd, int argc, char *argv[]) {
1831 struct agi_command *e;
1834 return RESULT_SHOWUSAGE;
1836 e = find_command(argv + 2, 1);
1838 ast_cli(fd, e->usage);
1840 if (find_command(argv + 2, -1)) {
1841 return help_workhorse(fd, argv + 1);
1843 join(fullcmd, sizeof(fullcmd), argv+1);
1844 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1848 return help_workhorse(fd, NULL);
1850 return RESULT_SUCCESS;
1853 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1854 struct agi_command *e;
1861 return RESULT_SHOWUSAGE;
1863 if (!(htmlfile = fopen(argv[2], "wt"))) {
1864 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1865 return RESULT_SHOWUSAGE;
1868 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1869 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1872 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1874 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1876 if (!commands[x].cmda[0]) break;
1879 join(fullcmd, sizeof(fullcmd), e->cmda);
1880 /* Hide commands that start with '_' */
1881 if (fullcmd[0] == '_')
1884 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1885 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1889 tempstr = strsep(&stringp, "\n");
1891 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1893 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1894 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1895 fprintf(htmlfile, "%s<BR>\n",tempstr);
1898 fprintf(htmlfile, "</TD></TR>\n");
1899 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1903 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1905 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1906 return RESULT_SUCCESS;
1909 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1912 struct localuser *u;
1913 char *argv[MAX_ARGS];
1915 char *tmp = (char *)buf;
1923 if (!data || ast_strlen_zero(data)) {
1924 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1927 ast_copy_string(buf, data, sizeof(buf));
1929 memset(&agi, 0, sizeof(agi));
1930 while ((stringp = strsep(&tmp, "|"))) {
1931 argv[argc++] = stringp;
1937 /* Answer if need be */
1938 if (chan->_state != AST_STATE_UP) {
1939 if (ast_answer(chan)) {
1940 LOCAL_USER_REMOVE(u);
1945 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1950 res = run_agi(chan, argv[0], &agi, pid, dead);
1955 LOCAL_USER_REMOVE(u);
1959 static int agi_exec(struct ast_channel *chan, void *data)
1961 if (chan->_softhangup)
1962 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1963 return agi_exec_full(chan, data, 0, 0);
1966 static int eagi_exec(struct ast_channel *chan, void *data)
1971 if (chan->_softhangup)
1972 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1973 readformat = chan->readformat;
1974 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1975 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1978 res = agi_exec_full(chan, data, 1, 0);
1980 if (ast_set_read_format(chan, readformat)) {
1981 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1987 static int deadagi_exec(struct ast_channel *chan, void *data)
1989 return agi_exec_full(chan, data, 0, 1);
1992 static char showagi_help[] =
1993 "Usage: show agi [topic]\n"
1994 " When called with a topic as an argument, displays usage\n"
1995 " information on the given command. If called without a\n"
1996 " topic, it provides a list of AGI commands.\n";
1999 static char dumpagihtml_help[] =
2000 "Usage: dump agihtml <filename>\n"
2001 " Dumps the agi command list in html format to given filename\n";
2003 static struct ast_cli_entry showagi =
2004 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
2006 static struct ast_cli_entry dumpagihtml =
2007 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
2009 int unload_module(void)
2011 STANDARD_HANGUP_LOCALUSERS;
2012 ast_cli_unregister(&showagi);
2013 ast_cli_unregister(&dumpagihtml);
2014 ast_cli_unregister(&cli_debug);
2015 ast_cli_unregister(&cli_no_debug);
2016 ast_unregister_application(eapp);
2017 ast_unregister_application(deadapp);
2018 return ast_unregister_application(app);
2021 int load_module(void)
2023 ast_cli_register(&showagi);
2024 ast_cli_register(&dumpagihtml);
2025 ast_cli_register(&cli_debug);
2026 ast_cli_register(&cli_no_debug);
2027 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
2028 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
2029 return ast_register_application(app, agi_exec, synopsis, descrip);
2032 char *description(void)
2040 STANDARD_USECOUNT(res);
2046 return ASTERISK_GPL_KEY;