2 * Asterisk -- A telephony toolkit for Linux.
4 * Asterisk Gateway Interface
6 * Copyright (C) 1999-2004, 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>
20 #include <asterisk/file.h>
21 #include <asterisk/logger.h>
22 #include <asterisk/channel.h>
23 #include <asterisk/pbx.h>
24 #include <asterisk/module.h>
25 #include <asterisk/astdb.h>
37 #include <asterisk/cli.h>
38 #include <asterisk/logger.h>
39 #include <asterisk/options.h>
40 #include <asterisk/image.h>
41 #include <asterisk/say.h>
42 #include <asterisk/app.h>
43 #include <asterisk/dsp.h>
44 #include <asterisk/musiconhold.h>
45 #include <asterisk/manager.h>
46 #include <asterisk/utils.h>
47 #include <asterisk/lock.h>
48 #include <asterisk/agi.h>
49 #include "../asterisk.h"
50 #include "../astconf.h"
53 #define MAX_COMMANDS 128
55 /* Recycle some stuff from the CLI interface */
56 #define fdprintf agi_debug_cli
58 static char *tdesc = "Asterisk Gateway Interface (AGI)";
60 static char *app = "AGI";
62 static char *eapp = "EAGI";
64 static char *deadapp = "DeadAGI";
66 static char *synopsis = "Executes an AGI compliant application";
67 static char *esynopsis = "Executes an EAGI compliant application";
68 static char *deadsynopsis = "Executes AGI on a hungup channel";
70 static char *descrip =
71 " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
72 "program on a channel. AGI allows Asterisk to launch external programs\n"
73 "written in any language to control a telephony channel, play audio,\n"
74 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
76 "Returns -1 on hangup (except for DeadAGI) or if application requested\n"
77 " hangup, or 0 on non-hangup exit. \n"
78 "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
79 "on file descriptor 3\n\n"
80 "Use the CLI command 'show agi' to list available agi commands\n";
82 static int agidebug = 0;
89 #define TONE_BLOCK_SIZE 200
91 /* Max time to connect to an AGI remote host */
92 #define MAX_AGI_CONNECT 2000
96 static void agi_debug_cli(int fd, char *fmt, ...)
103 res = vasprintf(&stuff, fmt, ap);
106 ast_log(LOG_ERROR, "Out of memory\n");
109 ast_verbose("AGI Tx >> %s", stuff);
110 ast_carefulwrite(fd, stuff, strlen(stuff), 100);
115 static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
119 struct pollfd pfds[1];
121 char *c; int port = AGI_PORT;
123 struct sockaddr_in sin;
125 struct ast_hostent ahp;
126 ast_log(LOG_DEBUG, "Blah\n");
127 host = ast_strdupa(agiurl + 6);
130 /* Strip off any script name */
131 if ((c = strchr(host, '/'))) {
136 if ((c = strchr(host, ':'))) {
142 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
145 hp = ast_gethostbyname(host, &ahp);
147 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
150 s = socket(AF_INET, SOCK_STREAM, 0);
152 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
155 flags = fcntl(s, F_GETFL);
157 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
161 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
162 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
166 memset(&sin, 0, sizeof(sin));
167 sin.sin_family = AF_INET;
168 sin.sin_port = htons(port);
169 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
170 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
171 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
176 pfds[0].events = POLLOUT;
177 if (poll(pfds, 1, MAX_AGI_CONNECT) != 1) {
178 ast_log(LOG_WARNING, "Connect to '%s' failed!\n", agiurl);
182 if (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) {
183 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
187 ast_log(LOG_DEBUG, "Wow, connected!\n");
194 static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
204 if (!strncasecmp(script, "agi://", 6))
205 return launch_netscript(script, argv, fds, efd, opid);
207 if (script[0] != '/') {
208 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
212 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
216 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
223 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
230 res = fcntl(audio[1], F_GETFL);
232 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
234 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
246 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
250 /* Redirect stdin and out, provide enhanced audio channel if desired */
251 dup2(fromast[0], STDIN_FILENO);
252 dup2(toast[1], STDOUT_FILENO);
254 dup2(audio[0], STDERR_FILENO + 1);
256 close(STDERR_FILENO + 1);
258 /* Close everything but stdin/out/error */
259 for (x=STDERR_FILENO + 2;x<1024;x++)
263 /* Can't use ast_log since FD's are closed */
264 fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
267 if (option_verbose > 2)
268 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
274 /* close what we're not using in the parent */
288 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
290 /* Print initial environment, with agi_request always being the first
292 fdprintf(fd, "agi_request: %s\n", request);
293 fdprintf(fd, "agi_channel: %s\n", chan->name);
294 fdprintf(fd, "agi_language: %s\n", chan->language);
295 fdprintf(fd, "agi_type: %s\n", chan->type);
296 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
299 fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "unknown");
300 fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "unknown");
301 fdprintf(fd, "agi_rdnis: %s\n", chan->rdnis ? chan->rdnis : "unknown");
303 /* Context information */
304 fdprintf(fd, "agi_context: %s\n", chan->context);
305 fdprintf(fd, "agi_extension: %s\n", chan->exten);
306 fdprintf(fd, "agi_priority: %d\n", chan->priority);
307 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
309 /* User information */
310 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
312 /* End with empty return */
316 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
320 if (chan->_state != AST_STATE_UP) {
321 /* Answer the chan */
322 res = ast_answer(chan);
324 fdprintf(agi->fd, "200 result=%d\n", res);
326 return RESULT_SUCCESS;
328 return RESULT_FAILURE;
331 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
336 return RESULT_SHOWUSAGE;
337 if (sscanf(argv[3], "%i", &to) != 1)
338 return RESULT_SHOWUSAGE;
339 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
340 fdprintf(agi->fd, "200 result=%d\n", res);
342 return RESULT_SUCCESS;
344 return RESULT_FAILURE;
347 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
351 return RESULT_SHOWUSAGE;
352 /* At the moment, the parser (perhaps broken) returns with
353 the last argument PLUS the newline at the end of the input
354 buffer. This probably needs to be fixed, but I wont do that
355 because other stuff may break as a result. The right way
356 would probably be to strip off the trailing newline before
357 parsing, then here, add a newline at the end of the string
358 before sending it to ast_sendtext --DUDE */
359 res = ast_sendtext(chan, argv[2]);
360 fdprintf(agi->fd, "200 result=%d\n", res);
362 return RESULT_SUCCESS;
364 return RESULT_FAILURE;
367 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
371 return RESULT_SHOWUSAGE;
372 res = ast_recvchar(chan,atoi(argv[2]));
374 fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
375 return RESULT_SUCCESS;
378 fdprintf(agi->fd, "200 result=%d\n", res);
379 return RESULT_SUCCESS;
382 fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
383 return RESULT_FAILURE;
387 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
391 return RESULT_SHOWUSAGE;
392 if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
393 if (!strncasecmp(argv[2],"mate",4)) x = 2;
394 if (!strncasecmp(argv[2],"tdd",3)) x = 1;
395 res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
396 fdprintf(agi->fd, "200 result=%d\n", res);
398 return RESULT_SUCCESS;
400 return RESULT_FAILURE;
403 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
407 return RESULT_SHOWUSAGE;
408 res = ast_send_image(chan, argv[2]);
409 if (!ast_check_hangup(chan))
411 fdprintf(agi->fd, "200 result=%d\n", res);
413 return RESULT_SUCCESS;
415 return RESULT_FAILURE;
418 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
421 struct ast_filestream *fs;
422 long sample_offset = 0;
426 return RESULT_SHOWUSAGE;
428 return RESULT_SHOWUSAGE;
429 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
430 return RESULT_SHOWUSAGE;
432 fs = ast_openstream(chan, argv[2], chan->language);
434 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
435 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
436 return RESULT_FAILURE;
438 ast_seekstream(fs, 0, SEEK_END);
439 max_length = ast_tellstream(fs);
440 ast_seekstream(fs, sample_offset, SEEK_SET);
441 res = ast_applystream(chan, fs);
442 res = ast_playstream(fs);
444 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
446 return RESULT_SHOWUSAGE;
448 return RESULT_FAILURE;
450 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
451 /* this is to check for if ast_waitstream closed the stream, we probably are at
452 * the end of the stream, return that amount, else check for the amount */
453 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
454 ast_stopstream(chan);
456 /* Stop this command, don't print a result line, as there is a new command */
457 return RESULT_SUCCESS;
459 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
461 return RESULT_SUCCESS;
463 return RESULT_FAILURE;
466 /*--- handle_saynumber: Say number in various language syntaxes ---*/
467 /* Need to add option for gender here as well. Coders wanted */
468 /* While waiting, we're sending a (char *) NULL. */
469 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
474 return RESULT_SHOWUSAGE;
475 if (sscanf(argv[2], "%i", &num) != 1)
476 return RESULT_SHOWUSAGE;
477 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
479 return RESULT_SUCCESS;
480 fdprintf(agi->fd, "200 result=%d\n", res);
482 return RESULT_SUCCESS;
484 return RESULT_FAILURE;
487 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
493 return RESULT_SHOWUSAGE;
494 if (sscanf(argv[2], "%i", &num) != 1)
495 return RESULT_SHOWUSAGE;
497 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
498 if (res == 1) /* New command */
499 return RESULT_SUCCESS;
500 fdprintf(agi->fd, "200 result=%d\n", res);
502 return RESULT_SUCCESS;
504 return RESULT_FAILURE;
507 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
512 return RESULT_SHOWUSAGE;
513 if (sscanf(argv[2], "%i", &num) != 1)
514 return RESULT_SHOWUSAGE;
515 res = ast_say_time(chan, num, argv[3], chan->language);
517 return RESULT_SUCCESS;
518 fdprintf(agi->fd, "200 result=%d\n", res);
520 return RESULT_SUCCESS;
522 return RESULT_FAILURE;
525 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
530 return RESULT_SHOWUSAGE;
532 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
533 if (res == 1) /* New command */
534 return RESULT_SUCCESS;
535 fdprintf(agi->fd, "200 result=%d\n", res);
537 return RESULT_SUCCESS;
539 return RESULT_FAILURE;
542 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
550 return RESULT_SHOWUSAGE;
551 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
552 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
553 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
554 if (res == 2) /* New command */
555 return RESULT_SUCCESS;
557 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
559 fdprintf(agi->fd, "200 result=-1\n");
561 fdprintf(agi->fd, "200 result=%s\n", data);
563 return RESULT_SUCCESS;
565 return RESULT_FAILURE;
568 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
572 return RESULT_SHOWUSAGE;
573 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
574 fdprintf(agi->fd, "200 result=0\n");
575 return RESULT_SUCCESS;
578 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
581 return RESULT_SHOWUSAGE;
582 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
583 fdprintf(agi->fd, "200 result=0\n");
584 return RESULT_SUCCESS;
587 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
591 return RESULT_SHOWUSAGE;
592 if (sscanf(argv[2], "%i", &pri) != 1)
593 return RESULT_SHOWUSAGE;
594 chan->priority = pri - 1;
595 fdprintf(agi->fd, "200 result=0\n");
596 return RESULT_SUCCESS;
599 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
601 struct ast_filestream *fs;
603 struct timeval tv, start;
604 long sample_offset = 0;
608 struct ast_dsp *sildet=NULL; /* silence detector dsp */
609 int totalsilence = 0;
611 int silence = 0; /* amount of silence to allow */
612 int gotsilence = 0; /* did we timeout for silence? */
613 char *silencestr=NULL;
617 /* XXX EAGI FIXME XXX */
620 return RESULT_SHOWUSAGE;
621 if (sscanf(argv[5], "%i", &ms) != 1)
622 return RESULT_SHOWUSAGE;
625 silencestr = strchr(argv[6],'s');
626 if ((argc > 7) && (!silencestr))
627 silencestr = strchr(argv[7],'s');
628 if ((argc > 8) && (!silencestr))
629 silencestr = strchr(argv[8],'s');
632 if (strlen(silencestr) > 2) {
633 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
637 silence = atoi(silencestr);
645 rfmt = chan->readformat;
646 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
648 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
651 sildet = ast_dsp_new();
653 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
656 ast_dsp_set_threshold(sildet, 256);
659 /* backward compatibility, if no offset given, arg[6] would have been
660 * caught below and taken to be a beep, else if it is a digit then it is a
662 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
663 res = ast_streamfile(chan, "beep", chan->language);
665 if ((argc > 7) && (!strchr(argv[7], '=')))
666 res = ast_streamfile(chan, "beep", chan->language);
669 res = ast_waitstream(chan, argv[4]);
671 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
674 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
676 ast_dsp_free(sildet);
677 return RESULT_FAILURE;
681 ast_applystream(chan,fs);
682 /* really should have checks */
683 ast_seekstream(fs, sample_offset, SEEK_SET);
686 gettimeofday(&start, NULL);
687 gettimeofday(&tv, NULL);
688 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
689 res = ast_waitfor(chan, -1);
692 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
694 ast_dsp_free(sildet);
695 return RESULT_FAILURE;
699 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
702 ast_dsp_free(sildet);
703 return RESULT_FAILURE;
705 switch(f->frametype) {
707 if (strchr(argv[4], f->subclass)) {
708 /* This is an interrupting chracter */
709 sample_offset = ast_tellstream(fs);
710 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
714 ast_dsp_free(sildet);
715 return RESULT_SUCCESS;
718 case AST_FRAME_VOICE:
719 ast_writestream(fs, f);
720 /* this is a safe place to check progress since we know that fs
721 * is valid after a write, and it will then have our current
723 sample_offset = ast_tellstream(fs);
726 ast_dsp_silence(sildet, f, &dspsilence);
728 totalsilence = dspsilence;
732 if (totalsilence > silence) {
733 /* Ended happily with silence */
742 gettimeofday(&tv, NULL);
748 ast_stream_rewind(fs, silence-1000);
750 sample_offset = ast_tellstream(fs);
752 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
755 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
758 res = ast_set_read_format(chan, rfmt);
760 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
761 ast_dsp_free(sildet);
763 return RESULT_SUCCESS;
766 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
771 return RESULT_SHOWUSAGE;
772 if (sscanf(argv[2], "%d", &timeout) != 1)
773 return RESULT_SHOWUSAGE;
777 chan->whentohangup = time(NULL) + timeout;
779 chan->whentohangup = 0;
780 fdprintf(agi->fd, "200 result=0\n");
781 return RESULT_SUCCESS;
784 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
786 struct ast_channel *c;
788 /* no argument: hangup the current channel */
789 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
790 fdprintf(agi->fd, "200 result=1\n");
791 return RESULT_SUCCESS;
792 } else if (argc==2) {
793 /* one argument: look for info on the specified channel */
794 c = ast_channel_walk_locked(NULL);
796 if (strcasecmp(argv[1],c->name)==0) {
797 /* we have a matching channel */
798 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
799 fdprintf(agi->fd, "200 result=1\n");
800 ast_mutex_unlock(&c->lock);
801 return RESULT_SUCCESS;
803 ast_mutex_unlock(&c->lock);
804 c = ast_channel_walk_locked(c);
806 /* if we get this far no channel name matched the argument given */
807 fdprintf(agi->fd, "200 result=-1\n");
808 return RESULT_SUCCESS;
810 return RESULT_SHOWUSAGE;
814 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
820 return RESULT_SHOWUSAGE;
822 if (option_verbose > 2)
823 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
825 app = pbx_findapp(argv[1]);
828 res = pbx_exec(chan, app, argv[2], 1);
830 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
833 fdprintf(agi->fd, "200 result=%d\n", res);
838 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
841 ast_set_callerid(chan, argv[2], 0);
843 fdprintf(agi->fd, "200 result=1\n");
844 return RESULT_SUCCESS;
847 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
849 struct ast_channel *c;
851 /* no argument: supply info on the current channel */
852 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
853 return RESULT_SUCCESS;
854 } else if (argc==3) {
855 /* one argument: look for info on the specified channel */
856 c = ast_channel_walk_locked(NULL);
858 if (strcasecmp(argv[2],c->name)==0) {
859 fdprintf(agi->fd, "200 result=%d\n", c->_state);
860 ast_mutex_unlock(&c->lock);
861 return RESULT_SUCCESS;
863 ast_mutex_unlock(&c->lock);
864 c = ast_channel_walk_locked(c);
866 /* if we get this far no channel name matched the argument given */
867 fdprintf(agi->fd, "200 result=-1\n");
868 return RESULT_SUCCESS;
870 return RESULT_SHOWUSAGE;
874 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
877 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
879 fdprintf(agi->fd, "200 result=1\n");
880 return RESULT_SUCCESS;
883 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
887 if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])))
888 fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
890 fdprintf(agi->fd, "200 result=0\n");
892 return RESULT_SUCCESS;
895 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
901 return RESULT_SHOWUSAGE;
904 sscanf(argv[2], "%d", &level);
908 prefix = VERBOSE_PREFIX_4;
911 prefix = VERBOSE_PREFIX_3;
914 prefix = VERBOSE_PREFIX_2;
918 prefix = VERBOSE_PREFIX_1;
922 if (level <= option_verbose)
923 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
925 fdprintf(agi->fd, "200 result=1\n");
927 return RESULT_SUCCESS;
930 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
935 return RESULT_SHOWUSAGE;
936 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
938 fdprintf(agi->fd, "200 result=0\n");
940 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
942 return RESULT_SUCCESS;
945 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
949 return RESULT_SHOWUSAGE;
950 res = ast_db_put(argv[2], argv[3], argv[4]);
952 fdprintf(agi->fd, "200 result=0\n");
954 fdprintf(agi->fd, "200 result=1\n");
956 return RESULT_SUCCESS;
959 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
963 return RESULT_SHOWUSAGE;
964 res = ast_db_del(argv[2], argv[3]);
966 fdprintf(agi->fd, "200 result=0\n");
968 fdprintf(agi->fd, "200 result=1\n");
970 return RESULT_SUCCESS;
973 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
976 if ((argc < 3) || (argc > 4))
977 return RESULT_SHOWUSAGE;
979 res = ast_db_deltree(argv[2], argv[3]);
981 res = ast_db_deltree(argv[2], NULL);
984 fdprintf(agi->fd, "200 result=0\n");
986 fdprintf(agi->fd, "200 result=1\n");
987 return RESULT_SUCCESS;
990 static char debug_usage[] =
992 " Enables dumping of AGI transactions for debugging purposes\n";
994 static char no_debug_usage[] =
995 "Usage: agi no debug\n"
996 " Disables dumping of AGI transactions for debugging purposes\n";
998 static int agi_do_debug(int fd, int argc, char *argv[])
1001 return RESULT_SHOWUSAGE;
1003 ast_cli(fd, "AGI Debugging Enabled\n");
1004 return RESULT_SUCCESS;
1007 static int agi_no_debug(int fd, int argc, char *argv[])
1010 return RESULT_SHOWUSAGE;
1012 ast_cli(fd, "AGI Debugging Disabled\n");
1013 return RESULT_SUCCESS;
1016 static struct ast_cli_entry cli_debug =
1017 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1019 static struct ast_cli_entry cli_no_debug =
1020 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1022 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1024 fdprintf(agi->fd, "200 result=0\n");
1025 return RESULT_SUCCESS;
1028 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1030 if (!strncasecmp(argv[2],"on",2)) {
1032 ast_moh_start(chan, argv[3]);
1034 ast_moh_start(chan, NULL);
1036 if (!strncasecmp(argv[2],"off",3)) {
1039 fdprintf(agi->fd, "200 result=0\n");
1040 return RESULT_SUCCESS;
1043 static char usage_setmusic[] =
1044 " Usage: SET MUSIC ON <on|off> <class>\n"
1045 " Enables/Disables the music on hold generator. If <class> is\n"
1046 " not specified then the default music on hold class will be used.\n"
1047 " Always returns 0\n";
1049 static char usage_dbput[] =
1050 " Usage: DATABASE PUT <family> <key> <value>\n"
1051 " Adds or updates an entry in the Asterisk database for a\n"
1052 " given family, key, and value.\n"
1053 " Returns 1 if succesful, 0 otherwise\n";
1055 static char usage_dbget[] =
1056 " Usage: DATABASE GET <family> <key>\n"
1057 " Retrieves an entry in the Asterisk database for a\n"
1058 " given family and key.\n"
1059 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1060 " is set and returns the variable in parenthesis\n"
1061 " example return code: 200 result=1 (testvariable)\n";
1063 static char usage_dbdel[] =
1064 " Usage: DATABASE DEL <family> <key>\n"
1065 " Deletes an entry in the Asterisk database for a\n"
1066 " given family and key.\n"
1067 " Returns 1 if succesful, 0 otherwise\n";
1069 static char usage_dbdeltree[] =
1070 " Usage: DATABASE DELTREE <family> [keytree]\n"
1071 " Deletes a family or specific keytree withing a family\n"
1072 " in the Asterisk database.\n"
1073 " Returns 1 if succesful, 0 otherwise\n";
1075 static char usage_verbose[] =
1076 " Usage: VERBOSE <message> <level>\n"
1077 " Sends <message> to the console via verbose message system.\n"
1078 " <level> is the the verbose level (1-4)\n"
1079 " Always returns 1\n";
1081 static char usage_getvariable[] =
1082 " Usage: GET VARIABLE <variablename>\n"
1083 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1084 " is set and returns the variable in parenthesis\n"
1085 " example return code: 200 result=1 (testvariable)\n";
1087 static char usage_setvariable[] =
1088 " Usage: SET VARIABLE <variablename> <value>\n";
1090 static char usage_channelstatus[] =
1091 " Usage: CHANNEL STATUS [<channelname>]\n"
1092 " Returns the status of the specified channel.\n"
1093 " If no channel name is given the returns the status of the\n"
1094 " current channel.\n"
1096 " 0 Channel is down and available\n"
1097 " 1 Channel is down, but reserved\n"
1098 " 2 Channel is off hook\n"
1099 " 3 Digits (or equivalent) have been dialed\n"
1100 " 4 Line is ringing\n"
1101 " 5 Remote end is ringing\n"
1103 " 7 Line is busy\n";
1105 static char usage_setcallerid[] =
1106 " Usage: SET CALLERID <number>\n"
1107 " Changes the callerid of the current channel.\n";
1109 static char usage_exec[] =
1110 " Usage: EXEC <application> <options>\n"
1111 " Executes <application> with given <options>.\n"
1112 " Returns whatever the application returns, or -2 on failure to find application\n";
1114 static char usage_hangup[] =
1115 " Usage: HANGUP [<channelname>]\n"
1116 " Hangs up the specified channel.\n"
1117 " If no channel name is given, hangs up the current channel\n";
1119 static char usage_answer[] =
1121 " Answers channel if not already in answer state. Returns -1 on\n"
1122 " channel failure, or 0 if successful.\n";
1124 static char usage_waitfordigit[] =
1125 " Usage: WAIT FOR DIGIT <timeout>\n"
1126 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1127 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1128 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1129 " for the timeout value if you desire the call to block indefinitely.\n";
1131 static char usage_sendtext[] =
1132 " Usage: SEND TEXT \"<text to send>\"\n"
1133 " Sends the given text on a channel. Most channels do not support the\n"
1134 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1135 " support text transmission. Returns -1 only on error/hangup. Text\n"
1136 " consisting of greater than one word should be placed in quotes since the\n"
1137 " command only accepts a single argument.\n";
1139 static char usage_recvchar[] =
1140 " Usage: RECEIVE CHAR <timeout>\n"
1141 " Receives a character of text on a channel. Specify timeout to be the\n"
1142 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1143 " do not support the reception of text. Returns the decimal value of the character\n"
1144 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1145 " -1 only on error/hangup.\n";
1147 static char usage_tddmode[] =
1148 " Usage: TDD MODE <on|off>\n"
1149 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1150 " successful, or 0 if channel is not TDD-capable.\n";
1152 static char usage_sendimage[] =
1153 " Usage: SEND IMAGE <image>\n"
1154 " Sends the given image on a channel. Most channels do not support the\n"
1155 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1156 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1157 " should not include extensions.\n";
1159 static char usage_streamfile[] =
1160 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1161 " Send the given file, allowing playback to be interrupted by the given\n"
1162 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1163 " permitted. If sample offset is provided then the audio will seek to sample\n"
1164 " offset before play starts. Returns 0 if playback completes without a digit\n"
1165 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1166 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1167 " extension must not be included in the filename.\n";
1169 static char usage_saynumber[] =
1170 " Usage: SAY NUMBER <number> <escape digits>\n"
1171 " Say a given number, returning early if any of the given DTMF digits\n"
1172 " are received on the channel. Returns 0 if playback completes without a digit\n"
1173 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1174 " -1 on error/hangup.\n";
1176 static char usage_saydigits[] =
1177 " Usage: SAY DIGITS <number> <escape digits>\n"
1178 " Say a given digit string, returning early if any of the given DTMF digits\n"
1179 " are received on the channel. Returns 0 if playback completes without a digit\n"
1180 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1181 " -1 on error/hangup.\n";
1183 static char usage_saytime[] =
1184 " Usage: SAY TIME <time> <escape digits>\n"
1185 " Say a given time, returning early if any of the given DTMF digits are\n"
1186 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1187 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1188 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1189 " digit if one was pressed or -1 on error/hangup.\n";
1191 static char usage_sayphonetic[] =
1192 " Usage: SAY PHONETIC <string> <escape digits>\n"
1193 " Say a given character string with phonetics, returning early if any of the given DTMF digits\n"
1194 " are received on the channel. Returns 0 if playback completes without a digit\n"
1195 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1196 " -1 on error/hangup.\n";
1198 static char usage_getdata[] =
1199 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1200 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
1201 "from the channel at the other end.\n";
1203 static char usage_setcontext[] =
1204 " Usage: SET CONTEXT <desired context>\n"
1205 " Sets the context for continuation upon exiting the application.\n";
1207 static char usage_setextension[] =
1208 " Usage: SET EXTENSION <new extension>\n"
1209 " Changes the extension for continuation upon exiting the application.\n";
1211 static char usage_setpriority[] =
1212 " Usage: SET PRIORITY <num>\n"
1213 " Changes the priority for continuation upon exiting the application.\n";
1215 static char usage_recordfile[] =
1216 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1217 " Record to a file until a given dtmf digit in the sequence is received\n"
1218 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1219 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1220 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1221 " the offset without exceeding the end of the file. \"silence\" is the number\n"
1222 " of seconds of silence allowed before the function returns despite the\n"
1223 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1224 " preceeded by \"s=\" and is optional.\n";
1227 static char usage_autohangup[] =
1228 " Usage: SET AUTOHANGUP <time>\n"
1229 " Cause the channel to automatically hangup at <time> seconds in the\n"
1230 "future. Of course it can be hungup before then as well. Setting to\n"
1231 "0 will cause the autohangup feature to be disabled on this channel.\n";
1233 static char usage_noop[] =
1237 static agi_command commands[MAX_COMMANDS] = {
1238 { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1239 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1240 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1241 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1242 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1243 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1244 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1245 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1246 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1247 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1248 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1249 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1250 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1251 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1252 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1253 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1254 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1255 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1256 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1257 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1258 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1259 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1260 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1261 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1262 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1263 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1264 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1265 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1266 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1267 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1270 static void join(char *s, size_t len, char *w[])
1273 /* Join words into a string */
1278 for (x=0;w[x];x++) {
1280 strncat(s, " ", len - strlen(s) - 1);
1281 strncat(s, w[x], len - strlen(s) - 1);
1285 static int help_workhorse(int fd, char *match[])
1290 struct agi_command *e;
1292 join(matchstr, sizeof(matchstr), match);
1293 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1294 if (!commands[x].cmda[0]) break;
1297 join(fullcmd, sizeof(fullcmd), e->cmda);
1298 /* Hide commands that start with '_' */
1299 if (fullcmd[0] == '_')
1302 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1306 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1311 int agi_register(agi_command *agi)
1314 for (x=0;x<MAX_COMMANDS - 1;x++) {
1315 if (commands[x].cmda[0] == agi->cmda[0]) {
1316 ast_log(LOG_WARNING, "Command already registered!\n");
1320 for (x=0;x<MAX_COMMANDS - 1;x++) {
1321 if (!commands[x].cmda[0]) {
1326 ast_log(LOG_WARNING, "No more room for new commands!\n");
1330 void agi_unregister(agi_command *agi)
1333 for (x=0;x<MAX_COMMANDS - 1;x++) {
1334 if (commands[x].cmda[0] == agi->cmda[0]) {
1335 memset(&commands[x], 0, sizeof(agi_command));
1340 static agi_command *find_command(char *cmds[], int exact)
1345 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1346 if (!commands[x].cmda[0]) break;
1347 /* start optimistic */
1349 for (y=0;match && cmds[y]; y++) {
1350 /* If there are no more words in the command (and we're looking for
1351 an exact match) or there is a difference between the two words,
1352 then this is not a match */
1353 if (!commands[x].cmda[y] && !exact)
1355 /* don't segfault if the next part of a command doesn't exist */
1356 if (!commands[x].cmda[y]) return NULL;
1357 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1360 /* If more words are needed to complete the command then this is not
1361 a candidate (unless we're looking for a really inexact answer */
1362 if ((exact > -1) && commands[x].cmda[y])
1365 return &commands[x];
1371 static int parse_args(char *s, int *max, char *argv[])
1383 /* If it's escaped, put a literal quote */
1388 if (quoted && whitespace) {
1389 /* If we're starting a quote, coming off white space start a new word, too */
1397 if (!quoted && !escaped) {
1398 /* If we're not quoted, mark this as whitespace, and
1399 end the previous argument */
1403 /* Otherwise, just treat it as anything else */
1407 /* If we're escaped, print a literal, otherwise enable escaping */
1417 if (x >= MAX_ARGS -1) {
1418 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1421 /* Coming off of whitespace, start the next argument */
1430 /* Null terminate */
1437 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1439 char *argv[MAX_ARGS];
1444 parse_args(buf, &argc, argv);
1447 for (x=0;x<argc;x++)
1448 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1450 c = find_command(argv, 0);
1452 res = c->handler(chan, agi, argc, argv);
1454 case RESULT_SHOWUSAGE:
1455 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1456 fdprintf(agi->fd, c->usage);
1457 fdprintf(agi->fd, "520 End of proper usage.\n");
1459 case AST_PBX_KEEPALIVE:
1460 /* We've been asked to keep alive, so do so */
1461 return AST_PBX_KEEPALIVE;
1463 case RESULT_FAILURE:
1464 /* They've already given the failure. We've been hung up on so handle this
1469 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1474 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1476 struct ast_channel *c;
1479 int returnstatus = 0;
1480 struct ast_frame *f;
1483 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1484 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1487 if (!(readf = fdopen(agi->ctrl, "r"))) {
1488 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1495 setup_env(chan, request, agi->fd, (agi->audio > -1));
1498 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1501 /* Idle the channel until we get a command */
1504 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1508 /* If it's voice, write it to the audio pipe */
1509 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1510 /* Write, ignoring errors */
1511 write(agi->audio, f->data, f->datalen);
1515 } else if (outfd > -1) {
1517 if (!fgets(buf, sizeof(buf), readf)) {
1518 /* Program terminated */
1521 if (option_verbose > 2)
1522 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1523 /* No need to kill the pid anymore, since they closed us */
1527 /* get rid of trailing newline, if any */
1528 if (*buf && buf[strlen(buf) - 1] == '\n')
1529 buf[strlen(buf) - 1] = 0;
1531 ast_verbose("AGI Rx << %s\n", buf);
1532 returnstatus |= agi_handle_command(chan, agi, buf);
1533 /* If the handle_command returns -1, we need to stop */
1534 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1539 ast_log(LOG_WARNING, "No channel, no fd?\n");
1545 /* Notify process */
1549 return returnstatus;
1552 static int handle_showagi(int fd, int argc, char *argv[]) {
1553 struct agi_command *e;
1556 return RESULT_SHOWUSAGE;
1558 e = find_command(argv + 2, 1);
1560 ast_cli(fd, e->usage);
1562 if (find_command(argv + 2, -1)) {
1563 return help_workhorse(fd, argv + 1);
1565 join(fullcmd, sizeof(fullcmd), argv+1);
1566 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1570 return help_workhorse(fd, NULL);
1572 return RESULT_SUCCESS;
1575 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1576 struct agi_command *e;
1583 return RESULT_SHOWUSAGE;
1585 if (!(htmlfile = fopen(argv[2], "wt"))) {
1586 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1587 return RESULT_SHOWUSAGE;
1590 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1591 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1594 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1596 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1598 if (!commands[x].cmda[0]) break;
1601 join(fullcmd, sizeof(fullcmd), e->cmda);
1602 /* Hide commands that start with '_' */
1603 if (fullcmd[0] == '_')
1606 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1607 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1611 tempstr = strsep(&stringp, "\n");
1613 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1615 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1616 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1617 fprintf(htmlfile, "%s<BR>\n",tempstr);
1620 fprintf(htmlfile, "</TD></TR>\n");
1621 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1625 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1627 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1628 return RESULT_SUCCESS;
1631 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1634 struct localuser *u;
1635 char *argv[MAX_ARGS];
1637 char *tmp = (char *)buf;
1644 if (!data || ast_strlen_zero(data)) {
1645 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1648 strncpy(buf, data, sizeof(buf) - 1);
1650 memset(&agi, 0, sizeof(agi));
1651 while ((stringp = strsep(&tmp, "|"))) {
1652 argv[argc++] = stringp;
1658 /* Answer if need be */
1659 if (chan->_state != AST_STATE_UP) {
1660 if (ast_answer(chan)) {
1661 LOCAL_USER_REMOVE(u);
1666 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1671 res = run_agi(chan, argv[0], &agi, pid, dead);
1676 LOCAL_USER_REMOVE(u);
1680 static int agi_exec(struct ast_channel *chan, void *data)
1682 if (chan->_softhangup)
1683 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1684 return agi_exec_full(chan, data, 0, 0);
1687 static int eagi_exec(struct ast_channel *chan, void *data)
1691 if (chan->_softhangup)
1692 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1693 readformat = chan->readformat;
1694 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1695 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1698 res = agi_exec_full(chan, data, 1, 0);
1700 if (ast_set_read_format(chan, readformat)) {
1701 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1707 static int deadagi_exec(struct ast_channel *chan, void *data)
1709 return agi_exec_full(chan, data, 0, 1);
1712 static char showagi_help[] =
1713 "Usage: show agi [topic]\n"
1714 " When called with a topic as an argument, displays usage\n"
1715 " information on the given command. If called without a\n"
1716 " topic, it provides a list of AGI commands.\n";
1719 static char dumpagihtml_help[] =
1720 "Usage: dump agihtml <filename>\n"
1721 " Dumps the agi command list in html format to given filename\n";
1723 static struct ast_cli_entry showagi =
1724 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1726 static struct ast_cli_entry dumpagihtml =
1727 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1729 int unload_module(void)
1731 STANDARD_HANGUP_LOCALUSERS;
1732 ast_cli_unregister(&showagi);
1733 ast_cli_unregister(&dumpagihtml);
1734 ast_cli_unregister(&cli_debug);
1735 ast_cli_unregister(&cli_no_debug);
1736 ast_unregister_application(eapp);
1737 ast_unregister_application(deadapp);
1738 return ast_unregister_application(app);
1741 int load_module(void)
1743 ast_cli_register(&showagi);
1744 ast_cli_register(&dumpagihtml);
1745 ast_cli_register(&cli_debug);
1746 ast_cli_register(&cli_no_debug);
1747 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
1748 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1749 return ast_register_application(app, agi_exec, synopsis, descrip);
1752 char *description(void)
1760 STANDARD_USECOUNT(res);
1766 return ASTERISK_GPL_KEY;