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>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/astdb.h>
32 #include <sys/socket.h>
33 #include <asterisk/cli.h>
34 #include <asterisk/logger.h>
35 #include <asterisk/options.h>
36 #include <asterisk/image.h>
37 #include <asterisk/say.h>
38 #include <asterisk/app.h>
39 #include <asterisk/dsp.h>
40 #include <asterisk/musiconhold.h>
41 #include <asterisk/manager.h>
42 #include <asterisk/utils.h>
43 #include <asterisk/lock.h>
44 #include <asterisk/agi.h>
45 #include "../asterisk.h"
46 #include "../astconf.h"
49 #define MAX_COMMANDS 128
51 /* Recycle some stuff from the CLI interface */
52 #define fdprintf agi_debug_cli
54 static char *tdesc = "Asterisk Gateway Interface (AGI)";
56 static char *app = "AGI";
58 static char *eapp = "EAGI";
60 static char *deadapp = "DeadAGI";
62 static char *synopsis = "Executes an AGI compliant application";
63 static char *esynopsis = "Executes an EAGI compliant application";
64 static char *deadsynopsis = "Executes AGI on a hungup channel";
66 static char *descrip =
67 " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
68 "program on a channel. AGI allows Asterisk to launch external programs\n"
69 "written in any language to control a telephony channel, play audio,\n"
70 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
72 "Returns -1 on hangup (except for DeadAGI) or if application requested\n"
73 " hangup, or 0 on non-hangup exit. \n"
74 "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
75 "on file descriptor 3\n\n"
76 "Use the CLI command 'show agi' to list available agi commands\n";
78 static int agidebug = 0;
85 #define TONE_BLOCK_SIZE 200
87 /* Max time to connect to an AGI remote host */
88 #define MAX_AGI_CONNECT 2000
92 static void agi_debug_cli(int fd, char *fmt, ...)
99 res = vasprintf(&stuff, fmt, ap);
102 ast_log(LOG_ERROR, "Out of memory\n");
105 ast_verbose("AGI Tx >> %s", stuff);
106 ast_carefulwrite(fd, stuff, strlen(stuff), 100);
111 static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
115 struct pollfd pfds[1];
117 char *c; int port = AGI_PORT;
119 struct sockaddr_in sin;
121 struct ast_hostent ahp;
122 ast_log(LOG_DEBUG, "Blah\n");
123 host = ast_strdupa(agiurl + 6);
126 /* Strip off any script name */
127 if ((c = strchr(host, '/'))) {
132 if ((c = strchr(host, ':'))) {
138 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
141 hp = ast_gethostbyname(host, &ahp);
143 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
146 s = socket(AF_INET, SOCK_STREAM, 0);
148 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
151 flags = fcntl(s, F_GETFL);
153 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
157 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
158 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
162 memset(&sin, 0, sizeof(sin));
163 sin.sin_family = AF_INET;
164 sin.sin_port = htons(port);
165 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
166 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
167 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
172 pfds[0].events = POLLOUT;
173 if (poll(pfds, 1, MAX_AGI_CONNECT) != 1) {
174 ast_log(LOG_WARNING, "Connect to '%s' failed!\n", agiurl);
178 if (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) {
179 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
183 ast_log(LOG_DEBUG, "Wow, connected!\n");
190 static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
200 if (!strncasecmp(script, "agi://", 6))
201 return launch_netscript(script, argv, fds, efd, opid);
203 if (script[0] != '/') {
204 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
208 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
212 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
219 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
226 res = fcntl(audio[1], F_GETFL);
228 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
230 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
242 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
246 /* Redirect stdin and out, provide enhanced audio channel if desired */
247 dup2(fromast[0], STDIN_FILENO);
248 dup2(toast[1], STDOUT_FILENO);
250 dup2(audio[0], STDERR_FILENO + 1);
252 close(STDERR_FILENO + 1);
254 /* Close everything but stdin/out/error */
255 for (x=STDERR_FILENO + 2;x<1024;x++)
259 /* Can't use ast_log since FD's are closed */
260 fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
263 if (option_verbose > 2)
264 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
270 /* close what we're not using in the parent */
284 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
286 /* Print initial environment, with agi_request always being the first
288 fdprintf(fd, "agi_request: %s\n", request);
289 fdprintf(fd, "agi_channel: %s\n", chan->name);
290 fdprintf(fd, "agi_language: %s\n", chan->language);
291 fdprintf(fd, "agi_type: %s\n", chan->type);
292 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
295 fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "unknown");
296 fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "unknown");
297 fdprintf(fd, "agi_rdnis: %s\n", chan->rdnis ? chan->rdnis : "unknown");
299 /* Context information */
300 fdprintf(fd, "agi_context: %s\n", chan->context);
301 fdprintf(fd, "agi_extension: %s\n", chan->exten);
302 fdprintf(fd, "agi_priority: %d\n", chan->priority);
303 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
305 /* User information */
306 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
308 /* End with empty return */
312 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
316 if (chan->_state != AST_STATE_UP) {
317 /* Answer the chan */
318 res = ast_answer(chan);
320 fdprintf(agi->fd, "200 result=%d\n", res);
322 return RESULT_SUCCESS;
324 return RESULT_FAILURE;
327 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
332 return RESULT_SHOWUSAGE;
333 if (sscanf(argv[3], "%i", &to) != 1)
334 return RESULT_SHOWUSAGE;
335 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
336 fdprintf(agi->fd, "200 result=%d\n", res);
338 return RESULT_SUCCESS;
340 return RESULT_FAILURE;
343 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
347 return RESULT_SHOWUSAGE;
348 /* At the moment, the parser (perhaps broken) returns with
349 the last argument PLUS the newline at the end of the input
350 buffer. This probably needs to be fixed, but I wont do that
351 because other stuff may break as a result. The right way
352 would probably be to strip off the trailing newline before
353 parsing, then here, add a newline at the end of the string
354 before sending it to ast_sendtext --DUDE */
355 res = ast_sendtext(chan, argv[2]);
356 fdprintf(agi->fd, "200 result=%d\n", res);
358 return RESULT_SUCCESS;
360 return RESULT_FAILURE;
363 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
367 return RESULT_SHOWUSAGE;
368 res = ast_recvchar(chan,atoi(argv[2]));
370 fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
371 return RESULT_SUCCESS;
374 fdprintf(agi->fd, "200 result=%d\n", res);
375 return RESULT_SUCCESS;
378 fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
379 return RESULT_FAILURE;
383 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
387 return RESULT_SHOWUSAGE;
388 if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
389 if (!strncasecmp(argv[2],"mate",4)) x = 2;
390 if (!strncasecmp(argv[2],"tdd",3)) x = 1;
391 res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
392 fdprintf(agi->fd, "200 result=%d\n", res);
394 return RESULT_SUCCESS;
396 return RESULT_FAILURE;
399 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
403 return RESULT_SHOWUSAGE;
404 res = ast_send_image(chan, argv[2]);
405 if (!ast_check_hangup(chan))
407 fdprintf(agi->fd, "200 result=%d\n", res);
409 return RESULT_SUCCESS;
411 return RESULT_FAILURE;
414 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
417 struct ast_filestream *fs;
418 long sample_offset = 0;
422 return RESULT_SHOWUSAGE;
424 return RESULT_SHOWUSAGE;
425 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
426 return RESULT_SHOWUSAGE;
428 fs = ast_openstream(chan, argv[2], chan->language);
430 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
431 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
432 return RESULT_FAILURE;
434 ast_seekstream(fs, 0, SEEK_END);
435 max_length = ast_tellstream(fs);
436 ast_seekstream(fs, sample_offset, SEEK_SET);
437 res = ast_applystream(chan, fs);
438 res = ast_playstream(fs);
440 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
442 return RESULT_SHOWUSAGE;
444 return RESULT_FAILURE;
446 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
447 /* this is to check for if ast_waitstream closed the stream, we probably are at
448 * the end of the stream, return that amount, else check for the amount */
449 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
450 ast_stopstream(chan);
452 /* Stop this command, don't print a result line, as there is a new command */
453 return RESULT_SUCCESS;
455 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
457 return RESULT_SUCCESS;
459 return RESULT_FAILURE;
462 /*--- handle_saynumber: Say number in various language syntaxes ---*/
463 /* Need to add option for gender here as well. Coders wanted */
464 /* While waiting, we're sending a (char *) NULL. */
465 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
470 return RESULT_SHOWUSAGE;
471 if (sscanf(argv[2], "%i", &num) != 1)
472 return RESULT_SHOWUSAGE;
473 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
475 return RESULT_SUCCESS;
476 fdprintf(agi->fd, "200 result=%d\n", res);
478 return RESULT_SUCCESS;
480 return RESULT_FAILURE;
483 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
489 return RESULT_SHOWUSAGE;
490 if (sscanf(argv[2], "%i", &num) != 1)
491 return RESULT_SHOWUSAGE;
493 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
494 if (res == 1) /* New command */
495 return RESULT_SUCCESS;
496 fdprintf(agi->fd, "200 result=%d\n", res);
498 return RESULT_SUCCESS;
500 return RESULT_FAILURE;
503 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
508 return RESULT_SHOWUSAGE;
509 if (sscanf(argv[2], "%i", &num) != 1)
510 return RESULT_SHOWUSAGE;
511 res = ast_say_time(chan, num, argv[3], chan->language);
513 return RESULT_SUCCESS;
514 fdprintf(agi->fd, "200 result=%d\n", res);
516 return RESULT_SUCCESS;
518 return RESULT_FAILURE;
521 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
526 return RESULT_SHOWUSAGE;
528 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
529 if (res == 1) /* New command */
530 return RESULT_SUCCESS;
531 fdprintf(agi->fd, "200 result=%d\n", res);
533 return RESULT_SUCCESS;
535 return RESULT_FAILURE;
538 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
546 return RESULT_SHOWUSAGE;
547 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
548 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
549 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
550 if (res == 2) /* New command */
551 return RESULT_SUCCESS;
553 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
555 fdprintf(agi->fd, "200 result=-1\n");
557 fdprintf(agi->fd, "200 result=%s\n", data);
559 return RESULT_SUCCESS;
561 return RESULT_FAILURE;
564 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
568 return RESULT_SHOWUSAGE;
569 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
570 fdprintf(agi->fd, "200 result=0\n");
571 return RESULT_SUCCESS;
574 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
577 return RESULT_SHOWUSAGE;
578 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
579 fdprintf(agi->fd, "200 result=0\n");
580 return RESULT_SUCCESS;
583 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
587 return RESULT_SHOWUSAGE;
588 if (sscanf(argv[2], "%i", &pri) != 1)
589 return RESULT_SHOWUSAGE;
590 chan->priority = pri - 1;
591 fdprintf(agi->fd, "200 result=0\n");
592 return RESULT_SUCCESS;
595 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
597 struct ast_filestream *fs;
599 struct timeval tv, start;
600 long sample_offset = 0;
604 struct ast_dsp *sildet=NULL; /* silence detector dsp */
605 int totalsilence = 0;
607 int silence = 0; /* amount of silence to allow */
608 int gotsilence = 0; /* did we timeout for silence? */
609 char *silencestr=NULL;
613 /* XXX EAGI FIXME XXX */
616 return RESULT_SHOWUSAGE;
617 if (sscanf(argv[5], "%i", &ms) != 1)
618 return RESULT_SHOWUSAGE;
621 silencestr = strchr(argv[6],'s');
622 if ((argc > 7) && (!silencestr))
623 silencestr = strchr(argv[7],'s');
624 if ((argc > 8) && (!silencestr))
625 silencestr = strchr(argv[8],'s');
628 if (strlen(silencestr) > 2) {
629 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
633 silence = atoi(silencestr);
641 rfmt = chan->readformat;
642 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
644 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
647 sildet = ast_dsp_new();
649 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
652 ast_dsp_set_threshold(sildet, 256);
655 /* backward compatibility, if no offset given, arg[6] would have been
656 * caught below and taken to be a beep, else if it is a digit then it is a
658 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
659 res = ast_streamfile(chan, "beep", chan->language);
661 if ((argc > 7) && (!strchr(argv[7], '=')))
662 res = ast_streamfile(chan, "beep", chan->language);
665 res = ast_waitstream(chan, argv[4]);
667 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
670 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
672 ast_dsp_free(sildet);
673 return RESULT_FAILURE;
677 ast_applystream(chan,fs);
678 /* really should have checks */
679 ast_seekstream(fs, sample_offset, SEEK_SET);
682 gettimeofday(&start, NULL);
683 gettimeofday(&tv, NULL);
684 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
685 res = ast_waitfor(chan, -1);
688 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
690 ast_dsp_free(sildet);
691 return RESULT_FAILURE;
695 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
698 ast_dsp_free(sildet);
699 return RESULT_FAILURE;
701 switch(f->frametype) {
703 if (strchr(argv[4], f->subclass)) {
704 /* This is an interrupting chracter */
705 sample_offset = ast_tellstream(fs);
706 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
710 ast_dsp_free(sildet);
711 return RESULT_SUCCESS;
714 case AST_FRAME_VOICE:
715 ast_writestream(fs, f);
716 /* this is a safe place to check progress since we know that fs
717 * is valid after a write, and it will then have our current
719 sample_offset = ast_tellstream(fs);
722 ast_dsp_silence(sildet, f, &dspsilence);
724 totalsilence = dspsilence;
728 if (totalsilence > silence) {
729 /* Ended happily with silence */
738 gettimeofday(&tv, NULL);
744 ast_stream_rewind(fs, silence-1000);
746 sample_offset = ast_tellstream(fs);
748 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
751 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
754 res = ast_set_read_format(chan, rfmt);
756 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
757 ast_dsp_free(sildet);
759 return RESULT_SUCCESS;
762 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
767 return RESULT_SHOWUSAGE;
768 if (sscanf(argv[2], "%d", &timeout) != 1)
769 return RESULT_SHOWUSAGE;
773 chan->whentohangup = time(NULL) + timeout;
775 chan->whentohangup = 0;
776 fdprintf(agi->fd, "200 result=0\n");
777 return RESULT_SUCCESS;
780 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
782 struct ast_channel *c;
784 /* no argument: hangup the current channel */
785 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
786 fdprintf(agi->fd, "200 result=1\n");
787 return RESULT_SUCCESS;
788 } else if (argc==2) {
789 /* one argument: look for info on the specified channel */
790 c = ast_channel_walk_locked(NULL);
792 if (strcasecmp(argv[1],c->name)==0) {
793 /* we have a matching channel */
794 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
795 fdprintf(agi->fd, "200 result=1\n");
796 ast_mutex_unlock(&c->lock);
797 return RESULT_SUCCESS;
799 ast_mutex_unlock(&c->lock);
800 c = ast_channel_walk_locked(c);
802 /* if we get this far no channel name matched the argument given */
803 fdprintf(agi->fd, "200 result=-1\n");
804 return RESULT_SUCCESS;
806 return RESULT_SHOWUSAGE;
810 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
816 return RESULT_SHOWUSAGE;
818 if (option_verbose > 2)
819 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
821 app = pbx_findapp(argv[1]);
824 res = pbx_exec(chan, app, argv[2], 1);
826 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
829 fdprintf(agi->fd, "200 result=%d\n", res);
834 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
837 ast_set_callerid(chan, argv[2], 0);
839 fdprintf(agi->fd, "200 result=1\n");
840 return RESULT_SUCCESS;
843 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
845 struct ast_channel *c;
847 /* no argument: supply info on the current channel */
848 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
849 return RESULT_SUCCESS;
850 } else if (argc==3) {
851 /* one argument: look for info on the specified channel */
852 c = ast_channel_walk_locked(NULL);
854 if (strcasecmp(argv[2],c->name)==0) {
855 fdprintf(agi->fd, "200 result=%d\n", c->_state);
856 ast_mutex_unlock(&c->lock);
857 return RESULT_SUCCESS;
859 ast_mutex_unlock(&c->lock);
860 c = ast_channel_walk_locked(c);
862 /* if we get this far no channel name matched the argument given */
863 fdprintf(agi->fd, "200 result=-1\n");
864 return RESULT_SUCCESS;
866 return RESULT_SHOWUSAGE;
870 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
873 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
875 fdprintf(agi->fd, "200 result=1\n");
876 return RESULT_SUCCESS;
879 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
883 if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])))
884 fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
886 fdprintf(agi->fd, "200 result=0\n");
888 return RESULT_SUCCESS;
891 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
897 return RESULT_SHOWUSAGE;
900 sscanf(argv[2], "%d", &level);
904 prefix = VERBOSE_PREFIX_4;
907 prefix = VERBOSE_PREFIX_3;
910 prefix = VERBOSE_PREFIX_2;
914 prefix = VERBOSE_PREFIX_1;
918 if (level <= option_verbose)
919 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
921 fdprintf(agi->fd, "200 result=1\n");
923 return RESULT_SUCCESS;
926 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
931 return RESULT_SHOWUSAGE;
932 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
934 fdprintf(agi->fd, "200 result=0\n");
936 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
938 return RESULT_SUCCESS;
941 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
945 return RESULT_SHOWUSAGE;
946 res = ast_db_put(argv[2], argv[3], argv[4]);
948 fdprintf(agi->fd, "200 result=0\n");
950 fdprintf(agi->fd, "200 result=1\n");
952 return RESULT_SUCCESS;
955 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
959 return RESULT_SHOWUSAGE;
960 res = ast_db_del(argv[2], argv[3]);
962 fdprintf(agi->fd, "200 result=0\n");
964 fdprintf(agi->fd, "200 result=1\n");
966 return RESULT_SUCCESS;
969 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
972 if ((argc < 3) || (argc > 4))
973 return RESULT_SHOWUSAGE;
975 res = ast_db_deltree(argv[2], argv[3]);
977 res = ast_db_deltree(argv[2], NULL);
980 fdprintf(agi->fd, "200 result=0\n");
982 fdprintf(agi->fd, "200 result=1\n");
983 return RESULT_SUCCESS;
986 static char debug_usage[] =
988 " Enables dumping of AGI transactions for debugging purposes\n";
990 static char no_debug_usage[] =
991 "Usage: agi no debug\n"
992 " Disables dumping of AGI transactions for debugging purposes\n";
994 static int agi_do_debug(int fd, int argc, char *argv[])
997 return RESULT_SHOWUSAGE;
999 ast_cli(fd, "AGI Debugging Enabled\n");
1000 return RESULT_SUCCESS;
1003 static int agi_no_debug(int fd, int argc, char *argv[])
1006 return RESULT_SHOWUSAGE;
1008 ast_cli(fd, "AGI Debugging Disabled\n");
1009 return RESULT_SUCCESS;
1012 static struct ast_cli_entry cli_debug =
1013 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1015 static struct ast_cli_entry cli_no_debug =
1016 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1018 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1020 fdprintf(agi->fd, "200 result=0\n");
1021 return RESULT_SUCCESS;
1024 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1026 if (!strncasecmp(argv[2],"on",2)) {
1028 ast_moh_start(chan, argv[3]);
1030 ast_moh_start(chan, NULL);
1032 if (!strncasecmp(argv[2],"off",3)) {
1035 fdprintf(agi->fd, "200 result=0\n");
1036 return RESULT_SUCCESS;
1039 static char usage_setmusic[] =
1040 " Usage: SET MUSIC ON <on|off> <class>\n"
1041 " Enables/Disables the music on hold generator. If <class> is\n"
1042 " not specified then the default music on hold class will be used.\n"
1043 " Always returns 0\n";
1045 static char usage_dbput[] =
1046 " Usage: DATABASE PUT <family> <key> <value>\n"
1047 " Adds or updates an entry in the Asterisk database for a\n"
1048 " given family, key, and value.\n"
1049 " Returns 1 if succesful, 0 otherwise\n";
1051 static char usage_dbget[] =
1052 " Usage: DATABASE GET <family> <key>\n"
1053 " Retrieves an entry in the Asterisk database for a\n"
1054 " given family and key.\n"
1055 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1056 " is set and returns the variable in parenthesis\n"
1057 " example return code: 200 result=1 (testvariable)\n";
1059 static char usage_dbdel[] =
1060 " Usage: DATABASE DEL <family> <key>\n"
1061 " Deletes an entry in the Asterisk database for a\n"
1062 " given family and key.\n"
1063 " Returns 1 if succesful, 0 otherwise\n";
1065 static char usage_dbdeltree[] =
1066 " Usage: DATABASE DELTREE <family> [keytree]\n"
1067 " Deletes a family or specific keytree withing a family\n"
1068 " in the Asterisk database.\n"
1069 " Returns 1 if succesful, 0 otherwise\n";
1071 static char usage_verbose[] =
1072 " Usage: VERBOSE <message> <level>\n"
1073 " Sends <message> to the console via verbose message system.\n"
1074 " <level> is the the verbose level (1-4)\n"
1075 " Always returns 1\n";
1077 static char usage_getvariable[] =
1078 " Usage: GET VARIABLE <variablename>\n"
1079 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1080 " is set and returns the variable in parenthesis\n"
1081 " example return code: 200 result=1 (testvariable)\n";
1083 static char usage_setvariable[] =
1084 " Usage: SET VARIABLE <variablename> <value>\n";
1086 static char usage_channelstatus[] =
1087 " Usage: CHANNEL STATUS [<channelname>]\n"
1088 " Returns the status of the specified channel.\n"
1089 " If no channel name is given the returns the status of the\n"
1090 " current channel.\n"
1092 " 0 Channel is down and available\n"
1093 " 1 Channel is down, but reserved\n"
1094 " 2 Channel is off hook\n"
1095 " 3 Digits (or equivalent) have been dialed\n"
1096 " 4 Line is ringing\n"
1097 " 5 Remote end is ringing\n"
1099 " 7 Line is busy\n";
1101 static char usage_setcallerid[] =
1102 " Usage: SET CALLERID <number>\n"
1103 " Changes the callerid of the current channel.\n";
1105 static char usage_exec[] =
1106 " Usage: EXEC <application> <options>\n"
1107 " Executes <application> with given <options>.\n"
1108 " Returns whatever the application returns, or -2 on failure to find application\n";
1110 static char usage_hangup[] =
1111 " Usage: HANGUP [<channelname>]\n"
1112 " Hangs up the specified channel.\n"
1113 " If no channel name is given, hangs up the current channel\n";
1115 static char usage_answer[] =
1117 " Answers channel if not already in answer state. Returns -1 on\n"
1118 " channel failure, or 0 if successful.\n";
1120 static char usage_waitfordigit[] =
1121 " Usage: WAIT FOR DIGIT <timeout>\n"
1122 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1123 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1124 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1125 " for the timeout value if you desire the call to block indefinitely.\n";
1127 static char usage_sendtext[] =
1128 " Usage: SEND TEXT \"<text to send>\"\n"
1129 " Sends the given text on a channel. Most channels do not support the\n"
1130 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1131 " support text transmission. Returns -1 only on error/hangup. Text\n"
1132 " consisting of greater than one word should be placed in quotes since the\n"
1133 " command only accepts a single argument.\n";
1135 static char usage_recvchar[] =
1136 " Usage: RECEIVE CHAR <timeout>\n"
1137 " Receives a character of text on a channel. Specify timeout to be the\n"
1138 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1139 " do not support the reception of text. Returns the decimal value of the character\n"
1140 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1141 " -1 only on error/hangup.\n";
1143 static char usage_tddmode[] =
1144 " Usage: TDD MODE <on|off>\n"
1145 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1146 " successful, or 0 if channel is not TDD-capable.\n";
1148 static char usage_sendimage[] =
1149 " Usage: SEND IMAGE <image>\n"
1150 " Sends the given image on a channel. Most channels do not support the\n"
1151 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1152 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1153 " should not include extensions.\n";
1155 static char usage_streamfile[] =
1156 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1157 " Send the given file, allowing playback to be interrupted by the given\n"
1158 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1159 " permitted. If sample offset is provided then the audio will seek to sample\n"
1160 " offset before play starts. Returns 0 if playback completes without a digit\n"
1161 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1162 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1163 " extension must not be included in the filename.\n";
1165 static char usage_saynumber[] =
1166 " Usage: SAY NUMBER <number> <escape digits>\n"
1167 " Say a given number, returning early if any of the given DTMF digits\n"
1168 " are received on the channel. Returns 0 if playback completes without a digit\n"
1169 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1170 " -1 on error/hangup.\n";
1172 static char usage_saydigits[] =
1173 " Usage: SAY DIGITS <number> <escape digits>\n"
1174 " Say a given digit string, returning early if any of the given DTMF digits\n"
1175 " are received on the channel. Returns 0 if playback completes without a digit\n"
1176 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1177 " -1 on error/hangup.\n";
1179 static char usage_saytime[] =
1180 " Usage: SAY TIME <time> <escape digits>\n"
1181 " Say a given time, returning early if any of the given DTMF digits are\n"
1182 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1183 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1184 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1185 " digit if one was pressed or -1 on error/hangup.\n";
1187 static char usage_sayphonetic[] =
1188 " Usage: SAY PHONETIC <string> <escape digits>\n"
1189 " Say a given character string with phonetics, returning early if any of the given DTMF digits\n"
1190 " are received on the channel. Returns 0 if playback completes without a digit\n"
1191 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1192 " -1 on error/hangup.\n";
1194 static char usage_getdata[] =
1195 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1196 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
1197 "from the channel at the other end.\n";
1199 static char usage_setcontext[] =
1200 " Usage: SET CONTEXT <desired context>\n"
1201 " Sets the context for continuation upon exiting the application.\n";
1203 static char usage_setextension[] =
1204 " Usage: SET EXTENSION <new extension>\n"
1205 " Changes the extension for continuation upon exiting the application.\n";
1207 static char usage_setpriority[] =
1208 " Usage: SET PRIORITY <num>\n"
1209 " Changes the priority for continuation upon exiting the application.\n";
1211 static char usage_recordfile[] =
1212 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1213 " Record to a file until a given dtmf digit in the sequence is received\n"
1214 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1215 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1216 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1217 " the offset without exceeding the end of the file. \"silence\" is the number\n"
1218 " of seconds of silence allowed before the function returns despite the\n"
1219 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1220 " preceeded by \"s=\" and is optional.\n";
1223 static char usage_autohangup[] =
1224 " Usage: SET AUTOHANGUP <time>\n"
1225 " Cause the channel to automatically hangup at <time> seconds in the\n"
1226 "future. Of course it can be hungup before then as well. Setting to\n"
1227 "0 will cause the autohangup feature to be disabled on this channel.\n";
1229 static char usage_noop[] =
1233 static agi_command commands[MAX_COMMANDS] = {
1234 { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1235 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1236 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1237 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1238 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1239 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1240 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1241 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1242 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1243 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1244 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1245 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1246 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1247 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1248 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1249 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1250 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1251 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1252 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1253 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1254 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1255 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1256 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1257 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1258 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1259 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1260 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1261 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1262 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1263 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1266 static void join(char *s, size_t len, char *w[])
1269 /* Join words into a string */
1274 for (x=0;w[x];x++) {
1276 strncat(s, " ", len - strlen(s) - 1);
1277 strncat(s, w[x], len - strlen(s) - 1);
1281 static int help_workhorse(int fd, char *match[])
1286 struct agi_command *e;
1288 join(matchstr, sizeof(matchstr), match);
1289 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1290 if (!commands[x].cmda[0]) break;
1293 join(fullcmd, sizeof(fullcmd), e->cmda);
1294 /* Hide commands that start with '_' */
1295 if (fullcmd[0] == '_')
1298 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1302 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1307 int agi_register(agi_command *agi)
1310 for (x=0;x<MAX_COMMANDS - 1;x++) {
1311 if (commands[x].cmda[0] == agi->cmda[0]) {
1312 ast_log(LOG_WARNING, "Command already registered!\n");
1316 for (x=0;x<MAX_COMMANDS - 1;x++) {
1317 if (!commands[x].cmda[0]) {
1322 ast_log(LOG_WARNING, "No more room for new commands!\n");
1326 void agi_unregister(agi_command *agi)
1329 for (x=0;x<MAX_COMMANDS - 1;x++) {
1330 if (commands[x].cmda[0] == agi->cmda[0]) {
1331 memset(&commands[x], 0, sizeof(agi_command));
1336 static agi_command *find_command(char *cmds[], int exact)
1341 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1342 if (!commands[x].cmda[0]) break;
1343 /* start optimistic */
1345 for (y=0;match && cmds[y]; y++) {
1346 /* If there are no more words in the command (and we're looking for
1347 an exact match) or there is a difference between the two words,
1348 then this is not a match */
1349 if (!commands[x].cmda[y] && !exact)
1351 /* don't segfault if the next part of a command doesn't exist */
1352 if (!commands[x].cmda[y]) return NULL;
1353 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1356 /* If more words are needed to complete the command then this is not
1357 a candidate (unless we're looking for a really inexact answer */
1358 if ((exact > -1) && commands[x].cmda[y])
1361 return &commands[x];
1367 static int parse_args(char *s, int *max, char *argv[])
1379 /* If it's escaped, put a literal quote */
1384 if (quoted && whitespace) {
1385 /* If we're starting a quote, coming off white space start a new word, too */
1393 if (!quoted && !escaped) {
1394 /* If we're not quoted, mark this as whitespace, and
1395 end the previous argument */
1399 /* Otherwise, just treat it as anything else */
1403 /* If we're escaped, print a literal, otherwise enable escaping */
1413 if (x >= MAX_ARGS -1) {
1414 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1417 /* Coming off of whitespace, start the next argument */
1426 /* Null terminate */
1433 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1435 char *argv[MAX_ARGS];
1440 parse_args(buf, &argc, argv);
1443 for (x=0;x<argc;x++)
1444 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1446 c = find_command(argv, 0);
1448 res = c->handler(chan, agi, argc, argv);
1450 case RESULT_SHOWUSAGE:
1451 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1452 fdprintf(agi->fd, c->usage);
1453 fdprintf(agi->fd, "520 End of proper usage.\n");
1455 case AST_PBX_KEEPALIVE:
1456 /* We've been asked to keep alive, so do so */
1457 return AST_PBX_KEEPALIVE;
1459 case RESULT_FAILURE:
1460 /* They've already given the failure. We've been hung up on so handle this
1465 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1470 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1472 struct ast_channel *c;
1475 int returnstatus = 0;
1476 struct ast_frame *f;
1479 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1480 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1483 if (!(readf = fdopen(agi->ctrl, "r"))) {
1484 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1491 setup_env(chan, request, agi->fd, (agi->audio > -1));
1494 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1497 /* Idle the channel until we get a command */
1500 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1504 /* If it's voice, write it to the audio pipe */
1505 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1506 /* Write, ignoring errors */
1507 write(agi->audio, f->data, f->datalen);
1511 } else if (outfd > -1) {
1513 if (!fgets(buf, sizeof(buf), readf)) {
1514 /* Program terminated */
1517 if (option_verbose > 2)
1518 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1519 /* No need to kill the pid anymore, since they closed us */
1523 /* get rid of trailing newline, if any */
1524 if (*buf && buf[strlen(buf) - 1] == '\n')
1525 buf[strlen(buf) - 1] = 0;
1527 ast_verbose("AGI Rx << %s\n", buf);
1528 returnstatus |= agi_handle_command(chan, agi, buf);
1529 /* If the handle_command returns -1, we need to stop */
1530 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1535 ast_log(LOG_WARNING, "No channel, no fd?\n");
1541 /* Notify process */
1545 return returnstatus;
1548 static int handle_showagi(int fd, int argc, char *argv[]) {
1549 struct agi_command *e;
1552 return RESULT_SHOWUSAGE;
1554 e = find_command(argv + 2, 1);
1556 ast_cli(fd, e->usage);
1558 if (find_command(argv + 2, -1)) {
1559 return help_workhorse(fd, argv + 1);
1561 join(fullcmd, sizeof(fullcmd), argv+1);
1562 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1566 return help_workhorse(fd, NULL);
1568 return RESULT_SUCCESS;
1571 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1572 struct agi_command *e;
1579 return RESULT_SHOWUSAGE;
1581 if (!(htmlfile = fopen(argv[2], "wt"))) {
1582 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1583 return RESULT_SHOWUSAGE;
1586 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1587 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1590 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1592 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1594 if (!commands[x].cmda[0]) break;
1597 join(fullcmd, sizeof(fullcmd), e->cmda);
1598 /* Hide commands that start with '_' */
1599 if (fullcmd[0] == '_')
1602 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1603 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1607 tempstr = strsep(&stringp, "\n");
1609 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1611 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1612 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1613 fprintf(htmlfile, "%s<BR>\n",tempstr);
1616 fprintf(htmlfile, "</TD></TR>\n");
1617 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1621 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1623 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1624 return RESULT_SUCCESS;
1627 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1630 struct localuser *u;
1631 char *argv[MAX_ARGS];
1633 char *tmp = (char *)buf;
1640 if (!data || ast_strlen_zero(data)) {
1641 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1644 strncpy(buf, data, sizeof(buf) - 1);
1646 memset(&agi, 0, sizeof(agi));
1647 while ((stringp = strsep(&tmp, "|"))) {
1648 argv[argc++] = stringp;
1654 /* Answer if need be */
1655 if (chan->_state != AST_STATE_UP) {
1656 if (ast_answer(chan)) {
1657 LOCAL_USER_REMOVE(u);
1662 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1667 res = run_agi(chan, argv[0], &agi, pid, dead);
1672 LOCAL_USER_REMOVE(u);
1676 static int agi_exec(struct ast_channel *chan, void *data)
1678 if (chan->_softhangup)
1679 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1680 return agi_exec_full(chan, data, 0, 0);
1683 static int eagi_exec(struct ast_channel *chan, void *data)
1687 if (chan->_softhangup)
1688 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1689 readformat = chan->readformat;
1690 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1691 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1694 res = agi_exec_full(chan, data, 1, 0);
1696 if (ast_set_read_format(chan, readformat)) {
1697 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1703 static int deadagi_exec(struct ast_channel *chan, void *data)
1705 return agi_exec_full(chan, data, 0, 1);
1708 static char showagi_help[] =
1709 "Usage: show agi [topic]\n"
1710 " When called with a topic as an argument, displays usage\n"
1711 " information on the given command. If called without a\n"
1712 " topic, it provides a list of AGI commands.\n";
1715 static char dumpagihtml_help[] =
1716 "Usage: dump agihtml <filename>\n"
1717 " Dumps the agi command list in html format to given filename\n";
1719 static struct ast_cli_entry showagi =
1720 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1722 static struct ast_cli_entry dumpagihtml =
1723 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1725 int unload_module(void)
1727 STANDARD_HANGUP_LOCALUSERS;
1728 ast_cli_unregister(&showagi);
1729 ast_cli_unregister(&dumpagihtml);
1730 ast_cli_unregister(&cli_debug);
1731 ast_cli_unregister(&cli_no_debug);
1732 ast_unregister_application(eapp);
1733 ast_unregister_application(deadapp);
1734 return ast_unregister_application(app);
1737 int load_module(void)
1739 ast_cli_register(&showagi);
1740 ast_cli_register(&dumpagihtml);
1741 ast_cli_register(&cli_debug);
1742 ast_cli_register(&cli_no_debug);
1743 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
1744 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1745 return ast_register_application(app, agi_exec, synopsis, descrip);
1748 char *description(void)
1756 STANDARD_USECOUNT(res);
1762 return ASTERISK_GPL_KEY;