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>
26 #include <asterisk/callerid.h>
38 #include <asterisk/cli.h>
39 #include <asterisk/logger.h>
40 #include <asterisk/options.h>
41 #include <asterisk/image.h>
42 #include <asterisk/say.h>
43 #include <asterisk/app.h>
44 #include <asterisk/dsp.h>
45 #include <asterisk/musiconhold.h>
46 #include <asterisk/manager.h>
47 #include <asterisk/utils.h>
48 #include <asterisk/lock.h>
49 #include <asterisk/agi.h>
50 #include "../asterisk.h"
51 #include "../astconf.h"
54 #define MAX_COMMANDS 128
56 /* Recycle some stuff from the CLI interface */
57 #define fdprintf agi_debug_cli
59 static char *tdesc = "Asterisk Gateway Interface (AGI)";
61 static char *app = "AGI";
63 static char *eapp = "EAGI";
65 static char *deadapp = "DeadAGI";
67 static char *synopsis = "Executes an AGI compliant application";
68 static char *esynopsis = "Executes an EAGI compliant application";
69 static char *deadsynopsis = "Executes AGI on a hungup channel";
71 static char *descrip =
72 " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
73 "program on a channel. AGI allows Asterisk to launch external programs\n"
74 "written in any language to control a telephony channel, play audio,\n"
75 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
77 "Returns -1 on hangup (except for DeadAGI) or if application requested\n"
78 " hangup, or 0 on non-hangup exit. \n"
79 "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
80 "on file descriptor 3\n\n"
81 "Use the CLI command 'show agi' to list available agi commands\n";
83 static int agidebug = 0;
90 #define TONE_BLOCK_SIZE 200
92 /* Max time to connect to an AGI remote host */
93 #define MAX_AGI_CONNECT 2000
97 static void agi_debug_cli(int fd, char *fmt, ...)
104 res = vasprintf(&stuff, fmt, ap);
107 ast_log(LOG_ERROR, "Out of memory\n");
110 ast_verbose("AGI Tx >> %s", stuff);
111 ast_carefulwrite(fd, stuff, strlen(stuff), 100);
116 static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
120 struct pollfd pfds[1];
122 char *c; int port = AGI_PORT;
124 struct sockaddr_in sin;
126 struct ast_hostent ahp;
127 ast_log(LOG_DEBUG, "Blah\n");
128 host = ast_strdupa(agiurl + 6);
131 /* Strip off any script name */
132 if ((c = strchr(host, '/'))) {
137 if ((c = strchr(host, ':'))) {
143 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
146 hp = ast_gethostbyname(host, &ahp);
148 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
151 s = socket(AF_INET, SOCK_STREAM, 0);
153 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
156 flags = fcntl(s, F_GETFL);
158 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
162 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
163 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
167 memset(&sin, 0, sizeof(sin));
168 sin.sin_family = AF_INET;
169 sin.sin_port = htons(port);
170 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
171 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
172 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
177 pfds[0].events = POLLOUT;
178 if (poll(pfds, 1, MAX_AGI_CONNECT) != 1) {
179 ast_log(LOG_WARNING, "Connect to '%s' failed!\n", agiurl);
183 if (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) {
184 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
188 ast_log(LOG_DEBUG, "Wow, connected!\n");
195 static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
205 if (!strncasecmp(script, "agi://", 6))
206 return launch_netscript(script, argv, fds, efd, opid);
208 if (script[0] != '/') {
209 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
213 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
217 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
224 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
231 res = fcntl(audio[1], F_GETFL);
233 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
235 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
247 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
251 /* Redirect stdin and out, provide enhanced audio channel if desired */
252 dup2(fromast[0], STDIN_FILENO);
253 dup2(toast[1], STDOUT_FILENO);
255 dup2(audio[0], STDERR_FILENO + 1);
257 close(STDERR_FILENO + 1);
259 /* Close everything but stdin/out/error */
260 for (x=STDERR_FILENO + 2;x<1024;x++)
264 /* Can't use ast_log since FD's are closed */
265 fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
268 if (option_verbose > 2)
269 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
275 /* close what we're not using in the parent */
289 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
291 /* Print initial environment, with agi_request always being the first
293 fdprintf(fd, "agi_request: %s\n", request);
294 fdprintf(fd, "agi_channel: %s\n", chan->name);
295 fdprintf(fd, "agi_language: %s\n", chan->language);
296 fdprintf(fd, "agi_type: %s\n", chan->type);
297 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
300 fdprintf(fd, "agi_callerid: %s\n", chan->cid.cid_num ? chan->cid.cid_num : "unknown");
301 fdprintf(fd, "agi_calleridname: %s\n", chan->cid.cid_name ? chan->cid.cid_name : "unknown");
302 fdprintf(fd, "agi_dnid: %s\n", chan->cid.cid_dnid ? chan->cid.cid_dnid : "unknown");
303 fdprintf(fd, "agi_rdnis: %s\n", chan->cid.cid_rdnis ? chan->cid.cid_rdnis : "unknown");
305 /* Context information */
306 fdprintf(fd, "agi_context: %s\n", chan->context);
307 fdprintf(fd, "agi_extension: %s\n", chan->exten);
308 fdprintf(fd, "agi_priority: %d\n", chan->priority);
309 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
311 /* User information */
312 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
314 /* End with empty return */
318 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
322 if (chan->_state != AST_STATE_UP) {
323 /* Answer the chan */
324 res = ast_answer(chan);
326 fdprintf(agi->fd, "200 result=%d\n", res);
328 return RESULT_SUCCESS;
330 return RESULT_FAILURE;
333 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
338 return RESULT_SHOWUSAGE;
339 if (sscanf(argv[3], "%i", &to) != 1)
340 return RESULT_SHOWUSAGE;
341 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
342 fdprintf(agi->fd, "200 result=%d\n", res);
344 return RESULT_SUCCESS;
346 return RESULT_FAILURE;
349 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
353 return RESULT_SHOWUSAGE;
354 /* At the moment, the parser (perhaps broken) returns with
355 the last argument PLUS the newline at the end of the input
356 buffer. This probably needs to be fixed, but I wont do that
357 because other stuff may break as a result. The right way
358 would probably be to strip off the trailing newline before
359 parsing, then here, add a newline at the end of the string
360 before sending it to ast_sendtext --DUDE */
361 res = ast_sendtext(chan, argv[2]);
362 fdprintf(agi->fd, "200 result=%d\n", res);
364 return RESULT_SUCCESS;
366 return RESULT_FAILURE;
369 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
373 return RESULT_SHOWUSAGE;
374 res = ast_recvchar(chan,atoi(argv[2]));
376 fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
377 return RESULT_SUCCESS;
380 fdprintf(agi->fd, "200 result=%d\n", res);
381 return RESULT_SUCCESS;
384 fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
385 return RESULT_FAILURE;
389 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
393 return RESULT_SHOWUSAGE;
394 if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
395 if (!strncasecmp(argv[2],"mate",4)) x = 2;
396 if (!strncasecmp(argv[2],"tdd",3)) x = 1;
397 res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
398 fdprintf(agi->fd, "200 result=%d\n", res);
400 return RESULT_SUCCESS;
402 return RESULT_FAILURE;
405 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
409 return RESULT_SHOWUSAGE;
410 res = ast_send_image(chan, argv[2]);
411 if (!ast_check_hangup(chan))
413 fdprintf(agi->fd, "200 result=%d\n", res);
415 return RESULT_SUCCESS;
417 return RESULT_FAILURE;
420 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
423 struct ast_filestream *fs;
424 long sample_offset = 0;
428 return RESULT_SHOWUSAGE;
430 return RESULT_SHOWUSAGE;
431 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
432 return RESULT_SHOWUSAGE;
434 fs = ast_openstream(chan, argv[2], chan->language);
436 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
437 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
438 return RESULT_FAILURE;
440 ast_seekstream(fs, 0, SEEK_END);
441 max_length = ast_tellstream(fs);
442 ast_seekstream(fs, sample_offset, SEEK_SET);
443 res = ast_applystream(chan, fs);
444 res = ast_playstream(fs);
446 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
448 return RESULT_SHOWUSAGE;
450 return RESULT_FAILURE;
452 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
453 /* this is to check for if ast_waitstream closed the stream, we probably are at
454 * the end of the stream, return that amount, else check for the amount */
455 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
456 ast_stopstream(chan);
458 /* Stop this command, don't print a result line, as there is a new command */
459 return RESULT_SUCCESS;
461 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
463 return RESULT_SUCCESS;
465 return RESULT_FAILURE;
468 /*--- handle_saynumber: Say number in various language syntaxes ---*/
469 /* Need to add option for gender here as well. Coders wanted */
470 /* While waiting, we're sending a (char *) NULL. */
471 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
476 return RESULT_SHOWUSAGE;
477 if (sscanf(argv[2], "%i", &num) != 1)
478 return RESULT_SHOWUSAGE;
479 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
481 return RESULT_SUCCESS;
482 fdprintf(agi->fd, "200 result=%d\n", res);
484 return RESULT_SUCCESS;
486 return RESULT_FAILURE;
489 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
495 return RESULT_SHOWUSAGE;
496 if (sscanf(argv[2], "%i", &num) != 1)
497 return RESULT_SHOWUSAGE;
499 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
500 if (res == 1) /* New command */
501 return RESULT_SUCCESS;
502 fdprintf(agi->fd, "200 result=%d\n", res);
504 return RESULT_SUCCESS;
506 return RESULT_FAILURE;
509 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
514 return RESULT_SHOWUSAGE;
515 if (sscanf(argv[2], "%i", &num) != 1)
516 return RESULT_SHOWUSAGE;
517 res = ast_say_time(chan, num, argv[3], chan->language);
519 return RESULT_SUCCESS;
520 fdprintf(agi->fd, "200 result=%d\n", res);
522 return RESULT_SUCCESS;
524 return RESULT_FAILURE;
527 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
532 return RESULT_SHOWUSAGE;
534 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
535 if (res == 1) /* New command */
536 return RESULT_SUCCESS;
537 fdprintf(agi->fd, "200 result=%d\n", res);
539 return RESULT_SUCCESS;
541 return RESULT_FAILURE;
544 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
552 return RESULT_SHOWUSAGE;
553 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
554 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
555 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
556 if (res == 2) /* New command */
557 return RESULT_SUCCESS;
559 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
561 fdprintf(agi->fd, "200 result=-1\n");
563 fdprintf(agi->fd, "200 result=%s\n", data);
565 return RESULT_SUCCESS;
567 return RESULT_FAILURE;
570 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
574 return RESULT_SHOWUSAGE;
575 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
576 fdprintf(agi->fd, "200 result=0\n");
577 return RESULT_SUCCESS;
580 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
583 return RESULT_SHOWUSAGE;
584 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
585 fdprintf(agi->fd, "200 result=0\n");
586 return RESULT_SUCCESS;
589 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
593 return RESULT_SHOWUSAGE;
594 if (sscanf(argv[2], "%i", &pri) != 1)
595 return RESULT_SHOWUSAGE;
596 chan->priority = pri - 1;
597 fdprintf(agi->fd, "200 result=0\n");
598 return RESULT_SUCCESS;
601 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
603 struct ast_filestream *fs;
605 struct timeval tv, start;
606 long sample_offset = 0;
610 struct ast_dsp *sildet=NULL; /* silence detector dsp */
611 int totalsilence = 0;
613 int silence = 0; /* amount of silence to allow */
614 int gotsilence = 0; /* did we timeout for silence? */
615 char *silencestr=NULL;
619 /* XXX EAGI FIXME XXX */
622 return RESULT_SHOWUSAGE;
623 if (sscanf(argv[5], "%i", &ms) != 1)
624 return RESULT_SHOWUSAGE;
627 silencestr = strchr(argv[6],'s');
628 if ((argc > 7) && (!silencestr))
629 silencestr = strchr(argv[7],'s');
630 if ((argc > 8) && (!silencestr))
631 silencestr = strchr(argv[8],'s');
634 if (strlen(silencestr) > 2) {
635 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
639 silence = atoi(silencestr);
647 rfmt = chan->readformat;
648 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
650 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
653 sildet = ast_dsp_new();
655 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
658 ast_dsp_set_threshold(sildet, 256);
661 /* backward compatibility, if no offset given, arg[6] would have been
662 * caught below and taken to be a beep, else if it is a digit then it is a
664 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
665 res = ast_streamfile(chan, "beep", chan->language);
667 if ((argc > 7) && (!strchr(argv[7], '=')))
668 res = ast_streamfile(chan, "beep", chan->language);
671 res = ast_waitstream(chan, argv[4]);
673 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
676 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
678 ast_dsp_free(sildet);
679 return RESULT_FAILURE;
683 ast_applystream(chan,fs);
684 /* really should have checks */
685 ast_seekstream(fs, sample_offset, SEEK_SET);
688 gettimeofday(&start, NULL);
689 gettimeofday(&tv, NULL);
690 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
691 res = ast_waitfor(chan, -1);
694 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
696 ast_dsp_free(sildet);
697 return RESULT_FAILURE;
701 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
704 ast_dsp_free(sildet);
705 return RESULT_FAILURE;
707 switch(f->frametype) {
709 if (strchr(argv[4], f->subclass)) {
710 /* This is an interrupting chracter */
711 sample_offset = ast_tellstream(fs);
712 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
716 ast_dsp_free(sildet);
717 return RESULT_SUCCESS;
720 case AST_FRAME_VOICE:
721 ast_writestream(fs, f);
722 /* this is a safe place to check progress since we know that fs
723 * is valid after a write, and it will then have our current
725 sample_offset = ast_tellstream(fs);
728 ast_dsp_silence(sildet, f, &dspsilence);
730 totalsilence = dspsilence;
734 if (totalsilence > silence) {
735 /* Ended happily with silence */
744 gettimeofday(&tv, NULL);
750 ast_stream_rewind(fs, silence-1000);
752 sample_offset = ast_tellstream(fs);
754 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
757 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
760 res = ast_set_read_format(chan, rfmt);
762 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
763 ast_dsp_free(sildet);
765 return RESULT_SUCCESS;
768 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
773 return RESULT_SHOWUSAGE;
774 if (sscanf(argv[2], "%d", &timeout) != 1)
775 return RESULT_SHOWUSAGE;
779 chan->whentohangup = time(NULL) + timeout;
781 chan->whentohangup = 0;
782 fdprintf(agi->fd, "200 result=0\n");
783 return RESULT_SUCCESS;
786 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
788 struct ast_channel *c;
790 /* no argument: hangup the current channel */
791 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
792 fdprintf(agi->fd, "200 result=1\n");
793 return RESULT_SUCCESS;
794 } else if (argc==2) {
795 /* one argument: look for info on the specified channel */
796 c = ast_channel_walk_locked(NULL);
798 if (strcasecmp(argv[1],c->name)==0) {
799 /* we have a matching channel */
800 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
801 fdprintf(agi->fd, "200 result=1\n");
802 ast_mutex_unlock(&c->lock);
803 return RESULT_SUCCESS;
805 ast_mutex_unlock(&c->lock);
806 c = ast_channel_walk_locked(c);
808 /* if we get this far no channel name matched the argument given */
809 fdprintf(agi->fd, "200 result=-1\n");
810 return RESULT_SUCCESS;
812 return RESULT_SHOWUSAGE;
816 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
822 return RESULT_SHOWUSAGE;
824 if (option_verbose > 2)
825 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
827 app = pbx_findapp(argv[1]);
830 res = pbx_exec(chan, app, argv[2], 1);
832 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
835 fdprintf(agi->fd, "200 result=%d\n", res);
840 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
843 char *l = NULL, *n = NULL;
845 strncpy(tmp, argv[2], sizeof(tmp) - 1);
846 ast_callerid_parse(tmp, &n, &l);
848 ast_shrink_phone_number(l);
853 ast_set_callerid(chan, l, n, NULL);
856 fdprintf(agi->fd, "200 result=1\n");
857 return RESULT_SUCCESS;
860 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
862 struct ast_channel *c;
864 /* no argument: supply info on the current channel */
865 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
866 return RESULT_SUCCESS;
867 } else if (argc==3) {
868 /* one argument: look for info on the specified channel */
869 c = ast_channel_walk_locked(NULL);
871 if (strcasecmp(argv[2],c->name)==0) {
872 fdprintf(agi->fd, "200 result=%d\n", c->_state);
873 ast_mutex_unlock(&c->lock);
874 return RESULT_SUCCESS;
876 ast_mutex_unlock(&c->lock);
877 c = ast_channel_walk_locked(c);
879 /* if we get this far no channel name matched the argument given */
880 fdprintf(agi->fd, "200 result=-1\n");
881 return RESULT_SUCCESS;
883 return RESULT_SHOWUSAGE;
887 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
890 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
892 fdprintf(agi->fd, "200 result=1\n");
893 return RESULT_SUCCESS;
896 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
900 if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])))
901 fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
903 fdprintf(agi->fd, "200 result=0\n");
905 return RESULT_SUCCESS;
908 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
914 return RESULT_SHOWUSAGE;
917 sscanf(argv[2], "%d", &level);
921 prefix = VERBOSE_PREFIX_4;
924 prefix = VERBOSE_PREFIX_3;
927 prefix = VERBOSE_PREFIX_2;
931 prefix = VERBOSE_PREFIX_1;
935 if (level <= option_verbose)
936 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
938 fdprintf(agi->fd, "200 result=1\n");
940 return RESULT_SUCCESS;
943 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
948 return RESULT_SHOWUSAGE;
949 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
951 fdprintf(agi->fd, "200 result=0\n");
953 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
955 return RESULT_SUCCESS;
958 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
962 return RESULT_SHOWUSAGE;
963 res = ast_db_put(argv[2], argv[3], argv[4]);
965 fdprintf(agi->fd, "200 result=0\n");
967 fdprintf(agi->fd, "200 result=1\n");
969 return RESULT_SUCCESS;
972 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
976 return RESULT_SHOWUSAGE;
977 res = ast_db_del(argv[2], argv[3]);
979 fdprintf(agi->fd, "200 result=0\n");
981 fdprintf(agi->fd, "200 result=1\n");
983 return RESULT_SUCCESS;
986 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
989 if ((argc < 3) || (argc > 4))
990 return RESULT_SHOWUSAGE;
992 res = ast_db_deltree(argv[2], argv[3]);
994 res = ast_db_deltree(argv[2], NULL);
997 fdprintf(agi->fd, "200 result=0\n");
999 fdprintf(agi->fd, "200 result=1\n");
1000 return RESULT_SUCCESS;
1003 static char debug_usage[] =
1004 "Usage: agi debug\n"
1005 " Enables dumping of AGI transactions for debugging purposes\n";
1007 static char no_debug_usage[] =
1008 "Usage: agi no debug\n"
1009 " Disables dumping of AGI transactions for debugging purposes\n";
1011 static int agi_do_debug(int fd, int argc, char *argv[])
1014 return RESULT_SHOWUSAGE;
1016 ast_cli(fd, "AGI Debugging Enabled\n");
1017 return RESULT_SUCCESS;
1020 static int agi_no_debug(int fd, int argc, char *argv[])
1023 return RESULT_SHOWUSAGE;
1025 ast_cli(fd, "AGI Debugging Disabled\n");
1026 return RESULT_SUCCESS;
1029 static struct ast_cli_entry cli_debug =
1030 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1032 static struct ast_cli_entry cli_no_debug =
1033 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1035 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1037 fdprintf(agi->fd, "200 result=0\n");
1038 return RESULT_SUCCESS;
1041 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1043 if (!strncasecmp(argv[2],"on",2)) {
1045 ast_moh_start(chan, argv[3]);
1047 ast_moh_start(chan, NULL);
1049 if (!strncasecmp(argv[2],"off",3)) {
1052 fdprintf(agi->fd, "200 result=0\n");
1053 return RESULT_SUCCESS;
1056 static char usage_setmusic[] =
1057 " Usage: SET MUSIC ON <on|off> <class>\n"
1058 " Enables/Disables the music on hold generator. If <class> is\n"
1059 " not specified then the default music on hold class will be used.\n"
1060 " Always returns 0\n";
1062 static char usage_dbput[] =
1063 " Usage: DATABASE PUT <family> <key> <value>\n"
1064 " Adds or updates an entry in the Asterisk database for a\n"
1065 " given family, key, and value.\n"
1066 " Returns 1 if succesful, 0 otherwise\n";
1068 static char usage_dbget[] =
1069 " Usage: DATABASE GET <family> <key>\n"
1070 " Retrieves an entry in the Asterisk database for a\n"
1071 " given family and key.\n"
1072 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1073 " is set and returns the variable in parenthesis\n"
1074 " example return code: 200 result=1 (testvariable)\n";
1076 static char usage_dbdel[] =
1077 " Usage: DATABASE DEL <family> <key>\n"
1078 " Deletes an entry in the Asterisk database for a\n"
1079 " given family and key.\n"
1080 " Returns 1 if succesful, 0 otherwise\n";
1082 static char usage_dbdeltree[] =
1083 " Usage: DATABASE DELTREE <family> [keytree]\n"
1084 " Deletes a family or specific keytree withing a family\n"
1085 " in the Asterisk database.\n"
1086 " Returns 1 if succesful, 0 otherwise\n";
1088 static char usage_verbose[] =
1089 " Usage: VERBOSE <message> <level>\n"
1090 " Sends <message> to the console via verbose message system.\n"
1091 " <level> is the the verbose level (1-4)\n"
1092 " Always returns 1\n";
1094 static char usage_getvariable[] =
1095 " Usage: GET VARIABLE <variablename>\n"
1096 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1097 " is set and returns the variable in parenthesis\n"
1098 " example return code: 200 result=1 (testvariable)\n";
1100 static char usage_setvariable[] =
1101 " Usage: SET VARIABLE <variablename> <value>\n";
1103 static char usage_channelstatus[] =
1104 " Usage: CHANNEL STATUS [<channelname>]\n"
1105 " Returns the status of the specified channel.\n"
1106 " If no channel name is given the returns the status of the\n"
1107 " current channel.\n"
1109 " 0 Channel is down and available\n"
1110 " 1 Channel is down, but reserved\n"
1111 " 2 Channel is off hook\n"
1112 " 3 Digits (or equivalent) have been dialed\n"
1113 " 4 Line is ringing\n"
1114 " 5 Remote end is ringing\n"
1116 " 7 Line is busy\n";
1118 static char usage_setcallerid[] =
1119 " Usage: SET CALLERID <number>\n"
1120 " Changes the callerid of the current channel.\n";
1122 static char usage_exec[] =
1123 " Usage: EXEC <application> <options>\n"
1124 " Executes <application> with given <options>.\n"
1125 " Returns whatever the application returns, or -2 on failure to find application\n";
1127 static char usage_hangup[] =
1128 " Usage: HANGUP [<channelname>]\n"
1129 " Hangs up the specified channel.\n"
1130 " If no channel name is given, hangs up the current channel\n";
1132 static char usage_answer[] =
1134 " Answers channel if not already in answer state. Returns -1 on\n"
1135 " channel failure, or 0 if successful.\n";
1137 static char usage_waitfordigit[] =
1138 " Usage: WAIT FOR DIGIT <timeout>\n"
1139 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1140 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1141 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1142 " for the timeout value if you desire the call to block indefinitely.\n";
1144 static char usage_sendtext[] =
1145 " Usage: SEND TEXT \"<text to send>\"\n"
1146 " Sends the given text on a channel. Most channels do not support the\n"
1147 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1148 " support text transmission. Returns -1 only on error/hangup. Text\n"
1149 " consisting of greater than one word should be placed in quotes since the\n"
1150 " command only accepts a single argument.\n";
1152 static char usage_recvchar[] =
1153 " Usage: RECEIVE CHAR <timeout>\n"
1154 " Receives a character of text on a channel. Specify timeout to be the\n"
1155 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1156 " do not support the reception of text. Returns the decimal value of the character\n"
1157 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1158 " -1 only on error/hangup.\n";
1160 static char usage_tddmode[] =
1161 " Usage: TDD MODE <on|off>\n"
1162 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1163 " successful, or 0 if channel is not TDD-capable.\n";
1165 static char usage_sendimage[] =
1166 " Usage: SEND IMAGE <image>\n"
1167 " Sends the given image on a channel. Most channels do not support the\n"
1168 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1169 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1170 " should not include extensions.\n";
1172 static char usage_streamfile[] =
1173 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1174 " Send the given file, allowing playback to be interrupted by the given\n"
1175 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1176 " permitted. If sample offset is provided then the audio will seek to sample\n"
1177 " offset before play starts. Returns 0 if playback completes without a digit\n"
1178 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1179 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1180 " extension must not be included in the filename.\n";
1182 static char usage_saynumber[] =
1183 " Usage: SAY NUMBER <number> <escape digits>\n"
1184 " Say a given number, returning early if any of the given DTMF digits\n"
1185 " are received on the channel. Returns 0 if playback completes without a digit\n"
1186 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1187 " -1 on error/hangup.\n";
1189 static char usage_saydigits[] =
1190 " Usage: SAY DIGITS <number> <escape digits>\n"
1191 " Say a given digit string, returning early if any of the given DTMF digits\n"
1192 " are received on the channel. Returns 0 if playback completes without a digit\n"
1193 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1194 " -1 on error/hangup.\n";
1196 static char usage_saytime[] =
1197 " Usage: SAY TIME <time> <escape digits>\n"
1198 " Say a given time, returning early if any of the given DTMF digits are\n"
1199 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1200 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1201 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1202 " digit if one was pressed or -1 on error/hangup.\n";
1204 static char usage_sayphonetic[] =
1205 " Usage: SAY PHONETIC <string> <escape digits>\n"
1206 " Say a given character string with phonetics, returning early if any of the given DTMF digits\n"
1207 " are received on the channel. Returns 0 if playback completes without a digit\n"
1208 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1209 " -1 on error/hangup.\n";
1211 static char usage_getdata[] =
1212 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1213 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
1214 "from the channel at the other end.\n";
1216 static char usage_setcontext[] =
1217 " Usage: SET CONTEXT <desired context>\n"
1218 " Sets the context for continuation upon exiting the application.\n";
1220 static char usage_setextension[] =
1221 " Usage: SET EXTENSION <new extension>\n"
1222 " Changes the extension for continuation upon exiting the application.\n";
1224 static char usage_setpriority[] =
1225 " Usage: SET PRIORITY <num>\n"
1226 " Changes the priority for continuation upon exiting the application.\n";
1228 static char usage_recordfile[] =
1229 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1230 " Record to a file until a given dtmf digit in the sequence is received\n"
1231 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1232 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1233 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1234 " the offset without exceeding the end of the file. \"silence\" is the number\n"
1235 " of seconds of silence allowed before the function returns despite the\n"
1236 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1237 " preceeded by \"s=\" and is optional.\n";
1240 static char usage_autohangup[] =
1241 " Usage: SET AUTOHANGUP <time>\n"
1242 " Cause the channel to automatically hangup at <time> seconds in the\n"
1243 "future. Of course it can be hungup before then as well. Setting to\n"
1244 "0 will cause the autohangup feature to be disabled on this channel.\n";
1246 static char usage_noop[] =
1250 static agi_command commands[MAX_COMMANDS] = {
1251 { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1252 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1253 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1254 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1255 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1256 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1257 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1258 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1259 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1260 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1261 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1262 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1263 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1264 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1265 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1266 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1267 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1268 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1269 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1270 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1271 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1272 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1273 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1274 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1275 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1276 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1277 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1278 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1279 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1280 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1283 static void join(char *s, size_t len, char *w[])
1286 /* Join words into a string */
1291 for (x=0;w[x];x++) {
1293 strncat(s, " ", len - strlen(s) - 1);
1294 strncat(s, w[x], len - strlen(s) - 1);
1298 static int help_workhorse(int fd, char *match[])
1303 struct agi_command *e;
1305 join(matchstr, sizeof(matchstr), match);
1306 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1307 if (!commands[x].cmda[0]) break;
1310 join(fullcmd, sizeof(fullcmd), e->cmda);
1311 /* Hide commands that start with '_' */
1312 if (fullcmd[0] == '_')
1315 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1319 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1324 int agi_register(agi_command *agi)
1327 for (x=0;x<MAX_COMMANDS - 1;x++) {
1328 if (commands[x].cmda[0] == agi->cmda[0]) {
1329 ast_log(LOG_WARNING, "Command already registered!\n");
1333 for (x=0;x<MAX_COMMANDS - 1;x++) {
1334 if (!commands[x].cmda[0]) {
1339 ast_log(LOG_WARNING, "No more room for new commands!\n");
1343 void agi_unregister(agi_command *agi)
1346 for (x=0;x<MAX_COMMANDS - 1;x++) {
1347 if (commands[x].cmda[0] == agi->cmda[0]) {
1348 memset(&commands[x], 0, sizeof(agi_command));
1353 static agi_command *find_command(char *cmds[], int exact)
1358 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1359 if (!commands[x].cmda[0]) break;
1360 /* start optimistic */
1362 for (y=0;match && cmds[y]; y++) {
1363 /* If there are no more words in the command (and we're looking for
1364 an exact match) or there is a difference between the two words,
1365 then this is not a match */
1366 if (!commands[x].cmda[y] && !exact)
1368 /* don't segfault if the next part of a command doesn't exist */
1369 if (!commands[x].cmda[y]) return NULL;
1370 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1373 /* If more words are needed to complete the command then this is not
1374 a candidate (unless we're looking for a really inexact answer */
1375 if ((exact > -1) && commands[x].cmda[y])
1378 return &commands[x];
1384 static int parse_args(char *s, int *max, char *argv[])
1396 /* If it's escaped, put a literal quote */
1401 if (quoted && whitespace) {
1402 /* If we're starting a quote, coming off white space start a new word, too */
1410 if (!quoted && !escaped) {
1411 /* If we're not quoted, mark this as whitespace, and
1412 end the previous argument */
1416 /* Otherwise, just treat it as anything else */
1420 /* If we're escaped, print a literal, otherwise enable escaping */
1430 if (x >= MAX_ARGS -1) {
1431 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1434 /* Coming off of whitespace, start the next argument */
1443 /* Null terminate */
1450 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1452 char *argv[MAX_ARGS];
1457 parse_args(buf, &argc, argv);
1460 for (x=0;x<argc;x++)
1461 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1463 c = find_command(argv, 0);
1465 res = c->handler(chan, agi, argc, argv);
1467 case RESULT_SHOWUSAGE:
1468 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1469 fdprintf(agi->fd, c->usage);
1470 fdprintf(agi->fd, "520 End of proper usage.\n");
1472 case AST_PBX_KEEPALIVE:
1473 /* We've been asked to keep alive, so do so */
1474 return AST_PBX_KEEPALIVE;
1476 case RESULT_FAILURE:
1477 /* They've already given the failure. We've been hung up on so handle this
1482 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1487 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1489 struct ast_channel *c;
1492 int returnstatus = 0;
1493 struct ast_frame *f;
1496 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1497 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1500 if (!(readf = fdopen(agi->ctrl, "r"))) {
1501 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1508 setup_env(chan, request, agi->fd, (agi->audio > -1));
1511 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1514 /* Idle the channel until we get a command */
1517 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1521 /* If it's voice, write it to the audio pipe */
1522 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1523 /* Write, ignoring errors */
1524 write(agi->audio, f->data, f->datalen);
1528 } else if (outfd > -1) {
1530 if (!fgets(buf, sizeof(buf), readf)) {
1531 /* Program terminated */
1534 if (option_verbose > 2)
1535 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1536 /* No need to kill the pid anymore, since they closed us */
1540 /* get rid of trailing newline, if any */
1541 if (*buf && buf[strlen(buf) - 1] == '\n')
1542 buf[strlen(buf) - 1] = 0;
1544 ast_verbose("AGI Rx << %s\n", buf);
1545 returnstatus |= agi_handle_command(chan, agi, buf);
1546 /* If the handle_command returns -1, we need to stop */
1547 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1552 ast_log(LOG_WARNING, "No channel, no fd?\n");
1558 /* Notify process */
1562 return returnstatus;
1565 static int handle_showagi(int fd, int argc, char *argv[]) {
1566 struct agi_command *e;
1569 return RESULT_SHOWUSAGE;
1571 e = find_command(argv + 2, 1);
1573 ast_cli(fd, e->usage);
1575 if (find_command(argv + 2, -1)) {
1576 return help_workhorse(fd, argv + 1);
1578 join(fullcmd, sizeof(fullcmd), argv+1);
1579 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1583 return help_workhorse(fd, NULL);
1585 return RESULT_SUCCESS;
1588 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1589 struct agi_command *e;
1596 return RESULT_SHOWUSAGE;
1598 if (!(htmlfile = fopen(argv[2], "wt"))) {
1599 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1600 return RESULT_SHOWUSAGE;
1603 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1604 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1607 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1609 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1611 if (!commands[x].cmda[0]) break;
1614 join(fullcmd, sizeof(fullcmd), e->cmda);
1615 /* Hide commands that start with '_' */
1616 if (fullcmd[0] == '_')
1619 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1620 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1624 tempstr = strsep(&stringp, "\n");
1626 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1628 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1629 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1630 fprintf(htmlfile, "%s<BR>\n",tempstr);
1633 fprintf(htmlfile, "</TD></TR>\n");
1634 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1638 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1640 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1641 return RESULT_SUCCESS;
1644 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1647 struct localuser *u;
1648 char *argv[MAX_ARGS];
1650 char *tmp = (char *)buf;
1657 if (!data || ast_strlen_zero(data)) {
1658 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1661 strncpy(buf, data, sizeof(buf) - 1);
1663 memset(&agi, 0, sizeof(agi));
1664 while ((stringp = strsep(&tmp, "|"))) {
1665 argv[argc++] = stringp;
1671 /* Answer if need be */
1672 if (chan->_state != AST_STATE_UP) {
1673 if (ast_answer(chan)) {
1674 LOCAL_USER_REMOVE(u);
1679 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1684 res = run_agi(chan, argv[0], &agi, pid, dead);
1689 LOCAL_USER_REMOVE(u);
1693 static int agi_exec(struct ast_channel *chan, void *data)
1695 if (chan->_softhangup)
1696 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1697 return agi_exec_full(chan, data, 0, 0);
1700 static int eagi_exec(struct ast_channel *chan, void *data)
1704 if (chan->_softhangup)
1705 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1706 readformat = chan->readformat;
1707 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1708 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1711 res = agi_exec_full(chan, data, 1, 0);
1713 if (ast_set_read_format(chan, readformat)) {
1714 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1720 static int deadagi_exec(struct ast_channel *chan, void *data)
1722 return agi_exec_full(chan, data, 0, 1);
1725 static char showagi_help[] =
1726 "Usage: show agi [topic]\n"
1727 " When called with a topic as an argument, displays usage\n"
1728 " information on the given command. If called without a\n"
1729 " topic, it provides a list of AGI commands.\n";
1732 static char dumpagihtml_help[] =
1733 "Usage: dump agihtml <filename>\n"
1734 " Dumps the agi command list in html format to given filename\n";
1736 static struct ast_cli_entry showagi =
1737 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1739 static struct ast_cli_entry dumpagihtml =
1740 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1742 int unload_module(void)
1744 STANDARD_HANGUP_LOCALUSERS;
1745 ast_cli_unregister(&showagi);
1746 ast_cli_unregister(&dumpagihtml);
1747 ast_cli_unregister(&cli_debug);
1748 ast_cli_unregister(&cli_no_debug);
1749 ast_unregister_application(eapp);
1750 ast_unregister_application(deadapp);
1751 return ast_unregister_application(app);
1754 int load_module(void)
1756 ast_cli_register(&showagi);
1757 ast_cli_register(&dumpagihtml);
1758 ast_cli_register(&cli_debug);
1759 ast_cli_register(&cli_no_debug);
1760 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
1761 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1762 return ast_register_application(app, agi_exec, synopsis, descrip);
1765 char *description(void)
1773 STANDARD_USECOUNT(res);
1779 return ASTERISK_GPL_KEY;