2 * Asterisk -- A telephony toolkit for Linux.
4 * Asterisk Gateway Interface
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
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>
31 #include <asterisk/cli.h>
32 #include <asterisk/logger.h>
33 #include <asterisk/options.h>
34 #include <asterisk/image.h>
35 #include <asterisk/say.h>
36 #include <asterisk/app.h>
37 #include <asterisk/dsp.h>
38 #include <asterisk/musiconhold.h>
39 #include "../asterisk.h"
40 #include "../astconf.h"
46 /* Recycle some stuff from the CLI interface */
47 #define fdprintf ast_cli
49 typedef struct agi_state {
50 int fd; /* FD for general output */
51 int audio; /* FD for audio output */
52 int ctrl; /* FD for input control */
55 typedef struct agi_command {
56 /* Null terminated list of the words of the command */
57 char *cmda[AST_MAX_CMD_LEN];
58 /* Handler for the command (channel, AGI state, # of arguments, argument list).
59 Returns RESULT_SHOWUSAGE for improper arguments */
60 int (*handler)(struct ast_channel *chan, AGI *agi, int argc, char *argv[]);
61 /* Summary of the command (< 60 characters) */
63 /* Detailed usage information */
67 static char *tdesc = "Asterisk Gateway Interface (AGI)";
69 static char *app = "AGI";
71 static char *eapp = "EAGI";
73 static char *synopsis = "Executes an AGI compliant application";
74 static char *esynopsis = "Executes an EAGI compliant application";
76 static char *descrip =
77 " [E]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
78 "program on a channel. AGI allows Asterisk to launch external programs\n"
79 "written in any language to control a telephony channel, play audio,\n"
80 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
82 "Returns -1 on hangup or if application requested hangup, or\n"
83 "0 on non-hangup exit. \n"
84 "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
85 "on file descriptor 3\n\n"
86 "Use the CLI command 'show agi' to list available agi commands\n";
93 #define TONE_BLOCK_SIZE 200
95 static int launch_script(char *script, char *args, int *fds, int *efd, int *opid)
104 if (script[0] != '/') {
105 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
109 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
113 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
120 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
127 res = fcntl(audio[1], F_GETFL);
129 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
131 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
143 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
147 /* Redirect stdin and out, provide enhanced audio channel if desired */
148 dup2(fromast[0], STDIN_FILENO);
149 dup2(toast[1], STDOUT_FILENO);
151 dup2(audio[0], STDERR_FILENO + 1);
153 close(STDERR_FILENO + 1);
155 /* Close everything but stdin/out/error */
156 for (x=STDERR_FILENO + 2;x<1024;x++)
159 execl(script, script, args, (char *)NULL);
160 /* Can't use ast_log since FD's are closed */
161 fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
164 if (option_verbose > 2)
165 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
171 /* close what we're not using in the parent */
185 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
187 /* Print initial environment, with agi_request always being the first
189 fdprintf(fd, "agi_request: %s\n", request);
190 fdprintf(fd, "agi_channel: %s\n", chan->name);
191 fdprintf(fd, "agi_language: %s\n", chan->language);
192 fdprintf(fd, "agi_type: %s\n", chan->type);
193 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
196 fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "unknown");
197 fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "unknown");
198 fdprintf(fd, "agi_rdnis: %s\n", chan->rdnis ? chan->rdnis : "unknown");
200 /* Context information */
201 fdprintf(fd, "agi_context: %s\n", chan->context);
202 fdprintf(fd, "agi_extension: %s\n", chan->exten);
203 fdprintf(fd, "agi_priority: %d\n", chan->priority);
204 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
206 /* User information */
207 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
209 /* End with empty return */
213 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
217 if (chan->_state != AST_STATE_UP) {
218 /* Answer the chan */
219 res = ast_answer(chan);
221 fdprintf(agi->fd, "200 result=%d\n", res);
223 return RESULT_SUCCESS;
225 return RESULT_FAILURE;
228 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
233 return RESULT_SHOWUSAGE;
234 if (sscanf(argv[3], "%i", &to) != 1)
235 return RESULT_SHOWUSAGE;
236 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
237 fdprintf(agi->fd, "200 result=%d\n", res);
239 return RESULT_SUCCESS;
241 return RESULT_FAILURE;
244 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
248 return RESULT_SHOWUSAGE;
249 /* At the moment, the parser (perhaps broken) returns with
250 the last argument PLUS the newline at the end of the input
251 buffer. This probably needs to be fixed, but I wont do that
252 because other stuff may break as a result. The right way
253 would probably be to strip off the trailing newline before
254 parsing, then here, add a newline at the end of the string
255 before sending it to ast_sendtext --DUDE */
256 res = ast_sendtext(chan, argv[2]);
257 fdprintf(agi->fd, "200 result=%d\n", res);
259 return RESULT_SUCCESS;
261 return RESULT_FAILURE;
264 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
268 return RESULT_SHOWUSAGE;
269 res = ast_recvchar(chan,atoi(argv[2]));
271 fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
272 return RESULT_SUCCESS;
275 fdprintf(agi->fd, "200 result=%d\n", res);
276 return RESULT_SUCCESS;
279 fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
280 return RESULT_FAILURE;
284 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
288 return RESULT_SHOWUSAGE;
289 if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
290 if (!strncasecmp(argv[2],"mate",4)) x = 2;
291 if (!strncasecmp(argv[2],"tdd",3)) x = 1;
292 res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
293 fdprintf(agi->fd, "200 result=%d\n", res);
295 return RESULT_SUCCESS;
297 return RESULT_FAILURE;
300 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
304 return RESULT_SHOWUSAGE;
305 res = ast_send_image(chan, argv[2]);
306 if (!ast_check_hangup(chan))
308 fdprintf(agi->fd, "200 result=%d\n", res);
310 return RESULT_SUCCESS;
312 return RESULT_FAILURE;
315 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
318 struct ast_filestream *fs;
319 long sample_offset = 0;
323 return RESULT_SHOWUSAGE;
325 return RESULT_SHOWUSAGE;
326 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
327 return RESULT_SHOWUSAGE;
329 fs = ast_openstream(chan, argv[2], chan->language);
331 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
332 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
333 return RESULT_FAILURE;
335 ast_seekstream(fs, 0, SEEK_END);
336 max_length = ast_tellstream(fs);
337 ast_seekstream(fs, sample_offset, SEEK_SET);
338 res = ast_applystream(chan, fs);
339 res = ast_playstream(fs);
341 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
343 return RESULT_SHOWUSAGE;
345 return RESULT_FAILURE;
347 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
348 /* this is to check for if ast_waitstream closed the stream, we probably are at
349 * the end of the stream, return that amount, else check for the amount */
350 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
351 ast_stopstream(chan);
353 /* Stop this command, don't print a result line, as there is a new command */
354 return RESULT_SUCCESS;
356 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
358 return RESULT_SUCCESS;
360 return RESULT_FAILURE;
363 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
368 return RESULT_SHOWUSAGE;
369 if (sscanf(argv[2], "%i", &num) != 1)
370 return RESULT_SHOWUSAGE;
371 res = ast_say_number_full(chan, num, argv[3], chan->language, agi->audio, agi->ctrl);
373 return RESULT_SUCCESS;
374 fdprintf(agi->fd, "200 result=%d\n", res);
376 return RESULT_SUCCESS;
378 return RESULT_FAILURE;
381 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
386 return RESULT_SHOWUSAGE;
387 if (sscanf(argv[2], "%i", &num) != 1)
388 return RESULT_SHOWUSAGE;
389 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
390 if (res == 1) /* New command */
391 return RESULT_SUCCESS;
392 fdprintf(agi->fd, "200 result=%d\n", res);
394 return RESULT_SUCCESS;
396 return RESULT_FAILURE;
399 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
404 return RESULT_SHOWUSAGE;
405 if (sscanf(argv[2], "%i", &num) != 1)
406 return RESULT_SHOWUSAGE;
407 res = ast_say_time(chan, num, argv[3], chan->language);
409 return RESULT_SUCCESS;
410 fdprintf(agi->fd, "200 result=%d\n", res);
412 return RESULT_SUCCESS;
414 return RESULT_FAILURE;
417 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
425 return RESULT_SHOWUSAGE;
426 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
427 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
428 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
429 if (res == 2) /* New command */
430 return RESULT_SUCCESS;
432 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
434 fdprintf(agi->fd, "200 result=-1\n");
436 fdprintf(agi->fd, "200 result=%s\n", data);
438 return RESULT_SUCCESS;
440 return RESULT_FAILURE;
443 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
447 return RESULT_SHOWUSAGE;
448 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
449 fdprintf(agi->fd, "200 result=0\n");
450 return RESULT_SUCCESS;
453 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
456 return RESULT_SHOWUSAGE;
457 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
458 fdprintf(agi->fd, "200 result=0\n");
459 return RESULT_SUCCESS;
462 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
466 return RESULT_SHOWUSAGE;
467 if (sscanf(argv[2], "%i", &pri) != 1)
468 return RESULT_SHOWUSAGE;
469 chan->priority = pri - 1;
470 fdprintf(agi->fd, "200 result=0\n");
471 return RESULT_SUCCESS;
474 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
476 struct ast_filestream *fs;
478 struct timeval tv, start;
479 long sample_offset = 0;
483 struct ast_dsp *sildet=NULL; /* silence detector dsp */
484 int totalsilence = 0;
486 int silence = 0; /* amount of silence to allow */
487 int gotsilence = 0; /* did we timeout for silence? */
488 char *silencestr=NULL;
492 /* XXX EAGI FIXME XXX */
495 return RESULT_SHOWUSAGE;
496 if (sscanf(argv[5], "%i", &ms) != 1)
497 return RESULT_SHOWUSAGE;
500 silencestr = strchr(argv[6],'s');
501 if ((argc > 7) && (!silencestr))
502 silencestr = strchr(argv[7],'s');
503 if ((argc > 8) && (!silencestr))
504 silencestr = strchr(argv[8],'s');
507 if (strlen(silencestr) > 2) {
508 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
512 silence = atoi(silencestr);
520 rfmt = chan->readformat;
521 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
523 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
526 sildet = ast_dsp_new();
528 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
531 ast_dsp_set_threshold(sildet, 256);
534 /* backward compatibility, if no offset given, arg[6] would have been
535 * caught below and taken to be a beep, else if it is a digit then it is a
537 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
538 res = ast_streamfile(chan, "beep", chan->language);
540 if ((argc > 7) && (!strchr(argv[7], '=')))
541 res = ast_streamfile(chan, "beep", chan->language);
544 res = ast_waitstream(chan, argv[4]);
546 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
549 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
550 return RESULT_FAILURE;
554 ast_applystream(chan,fs);
555 /* really should have checks */
556 ast_seekstream(fs, sample_offset, SEEK_SET);
559 gettimeofday(&start, NULL);
560 gettimeofday(&tv, NULL);
561 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
562 res = ast_waitfor(chan, -1);
565 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
566 return RESULT_FAILURE;
570 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
572 return RESULT_FAILURE;
574 switch(f->frametype) {
576 if (strchr(argv[4], f->subclass)) {
577 /* This is an interrupting chracter */
578 sample_offset = ast_tellstream(fs);
579 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
582 return RESULT_SUCCESS;
585 case AST_FRAME_VOICE:
586 ast_writestream(fs, f);
587 /* this is a safe place to check progress since we know that fs
588 * is valid after a write, and it will then have our current
590 sample_offset = ast_tellstream(fs);
593 ast_dsp_silence(sildet, f, &dspsilence);
595 totalsilence = dspsilence;
599 if (totalsilence > silence) {
600 /* Ended happily with silence */
609 gettimeofday(&tv, NULL);
615 ast_stream_rewind(fs, silence-1000);
618 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
621 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
624 res = ast_set_read_format(chan, rfmt);
626 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
627 ast_dsp_free(sildet);
629 return RESULT_SUCCESS;
632 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
637 return RESULT_SHOWUSAGE;
638 if (sscanf(argv[2], "%d", &timeout) != 1)
639 return RESULT_SHOWUSAGE;
643 chan->whentohangup = time(NULL) + timeout;
645 chan->whentohangup = 0;
646 fdprintf(agi->fd, "200 result=0\n");
647 return RESULT_SUCCESS;
650 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
652 struct ast_channel *c;
654 /* no argument: hangup the current channel */
655 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
656 fdprintf(agi->fd, "200 result=1\n");
657 return RESULT_SUCCESS;
658 } else if (argc==2) {
659 /* one argument: look for info on the specified channel */
660 c = ast_channel_walk(NULL);
662 if (strcasecmp(argv[1],c->name)==0) {
663 /* we have a matching channel */
664 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
665 fdprintf(agi->fd, "200 result=1\n");
666 return RESULT_SUCCESS;
668 c = ast_channel_walk(c);
670 /* if we get this far no channel name matched the argument given */
671 fdprintf(agi->fd, "200 result=-1\n");
672 return RESULT_SUCCESS;
674 return RESULT_SHOWUSAGE;
678 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
684 return RESULT_SHOWUSAGE;
686 if (option_verbose > 2)
687 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
689 app = pbx_findapp(argv[1]);
692 res = pbx_exec(chan, app, argv[2], 1);
694 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
697 fdprintf(agi->fd, "200 result=%d\n", res);
702 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
705 ast_set_callerid(chan, argv[2], 0);
707 /* strncpy(chan->callerid, argv[2], sizeof(chan->callerid)-1);
708 */ fdprintf(agi->fd, "200 result=1\n");
709 return RESULT_SUCCESS;
712 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
714 struct ast_channel *c;
716 /* no argument: supply info on the current channel */
717 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
718 return RESULT_SUCCESS;
719 } else if (argc==3) {
720 /* one argument: look for info on the specified channel */
721 c = ast_channel_walk(NULL);
723 if (strcasecmp(argv[2],c->name)==0) {
724 fdprintf(agi->fd, "200 result=%d\n", c->_state);
725 return RESULT_SUCCESS;
727 c = ast_channel_walk(c);
729 /* if we get this far no channel name matched the argument given */
730 fdprintf(agi->fd, "200 result=-1\n");
731 return RESULT_SUCCESS;
733 return RESULT_SHOWUSAGE;
737 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
740 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
742 fdprintf(agi->fd, "200 result=1\n");
743 return RESULT_SUCCESS;
746 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
750 if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])) )
751 fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
753 fdprintf(agi->fd, "200 result=0\n");
755 return RESULT_SUCCESS;
758 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
764 return RESULT_SHOWUSAGE;
767 sscanf(argv[2], "%d", &level);
771 prefix = VERBOSE_PREFIX_4;
774 prefix = VERBOSE_PREFIX_3;
777 prefix = VERBOSE_PREFIX_2;
781 prefix = VERBOSE_PREFIX_1;
785 if (level <= option_verbose)
786 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
788 fdprintf(agi->fd, "200 result=1\n");
790 return RESULT_SUCCESS;
793 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
798 return RESULT_SHOWUSAGE;
799 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
801 fdprintf(agi->fd, "200 result=0\n");
803 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
805 return RESULT_SUCCESS;
808 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
812 return RESULT_SHOWUSAGE;
813 res = ast_db_put(argv[2], argv[3], argv[4]);
815 fdprintf(agi->fd, "200 result=0\n");
817 fdprintf(agi->fd, "200 result=1\n");
819 return RESULT_SUCCESS;
822 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
826 return RESULT_SHOWUSAGE;
827 res = ast_db_del(argv[2], argv[3]);
829 fdprintf(agi->fd, "200 result=0\n");
831 fdprintf(agi->fd, "200 result=1\n");
833 return RESULT_SUCCESS;
836 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
839 if ((argc < 3) || (argc > 4))
840 return RESULT_SHOWUSAGE;
842 res = ast_db_deltree(argv[2], argv[3]);
844 res = ast_db_deltree(argv[2], NULL);
847 fdprintf(agi->fd, "200 result=0\n");
849 fdprintf(agi->fd, "200 result=1\n");
850 return RESULT_SUCCESS;
853 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
855 fdprintf(agi->fd, "200 result=0\n");
856 return RESULT_SUCCESS;
859 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
861 if (!strncasecmp(argv[2],"on",2)) {
863 ast_moh_start(chan, argv[3]);
865 ast_moh_start(chan, NULL);
867 if (!strncasecmp(argv[2],"off",3)) {
870 fdprintf(agi->fd, "200 result=0\n");
871 return RESULT_SUCCESS;
874 static char usage_setmusic[] =
875 " Usage: SET MUSIC ON <on|off> <class>\n"
876 " Enables/Disables the music on hold generator. If <class> is\n"
877 " not specified then the default music on hold class will be used.\n"
878 " Always returns 0\n";
880 static char usage_dbput[] =
881 " Usage: DATABASE PUT <family> <key> <value>\n"
882 " Adds or updates an entry in the Asterisk database for a\n"
883 " given family, key, and value.\n"
884 " Returns 1 if succesful, 0 otherwise\n";
886 static char usage_dbget[] =
887 " Usage: DATABASE GET <family> <key>\n"
888 " Retrieves an entry in the Asterisk database for a\n"
889 " given family and key.\n"
890 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
891 " is set and returns the variable in parenthesis\n"
892 " example return code: 200 result=1 (testvariable)\n";
894 static char usage_dbdel[] =
895 " Usage: DATABASE DEL <family> <key>\n"
896 " Deletes an entry in the Asterisk database for a\n"
897 " given family and key.\n"
898 " Returns 1 if succesful, 0 otherwise\n";
900 static char usage_dbdeltree[] =
901 " Usage: DATABASE DELTREE <family> [keytree]\n"
902 " Deletes a family or specific keytree withing a family\n"
903 " in the Asterisk database.\n"
904 " Returns 1 if succesful, 0 otherwise\n";
906 static char usage_verbose[] =
907 " Usage: VERBOSE <message> <level>\n"
908 " Sends <message> to the console via verbose message system.\n"
909 " <level> is the the verbose level (1-4)\n"
910 " Always returns 1\n";
912 static char usage_getvariable[] =
913 " Usage: GET VARIABLE <variablename>\n"
914 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
915 " is set and returns the variable in parenthesis\n"
916 " example return code: 200 result=1 (testvariable)\n";
918 static char usage_setvariable[] =
919 " Usage: SET VARIABLE <variablename> <value>\n";
921 static char usage_channelstatus[] =
922 " Usage: CHANNEL STATUS [<channelname>]\n"
923 " Returns the status of the specified channel.\n"
924 " If no channel name is given the returns the status of the\n"
925 " current channel.\n"
927 " 0 Channel is down and available\n"
928 " 1 Channel is down, but reserved\n"
929 " 2 Channel is off hook\n"
930 " 3 Digits (or equivalent) have been dialed\n"
931 " 4 Line is ringing\n"
932 " 5 Remote end is ringing\n"
936 static char usage_setcallerid[] =
937 " Usage: SET CALLERID <number>\n"
938 " Changes the callerid of the current channel.\n";
940 static char usage_exec[] =
941 " Usage: EXEC <application> <options>\n"
942 " Executes <application> with given <options>.\n"
943 " Returns whatever the application returns, or -2 on failure to find application\n";
945 static char usage_hangup[] =
946 " Usage: HANGUP [<channelname>]\n"
947 " Hangs up the specified channel.\n"
948 " If no channel name is given, hangs up the current channel\n";
950 static char usage_answer[] =
952 " Answers channel if not already in answer state. Returns -1 on\n"
953 " channel failure, or 0 if successful.\n";
955 static char usage_waitfordigit[] =
956 " Usage: WAIT FOR DIGIT <timeout>\n"
957 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
958 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
959 " the numerical value of the ascii of the digit if one is received. Use -1\n"
960 " for the timeout value if you desire the call to block indefinitely.\n";
962 static char usage_sendtext[] =
963 " Usage: SEND TEXT \"<text to send>\"\n"
964 " Sends the given text on a channel. Most channels do not support the\n"
965 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
966 " support text transmission. Returns -1 only on error/hangup. Text\n"
967 " consisting of greater than one word should be placed in quotes since the\n"
968 " command only accepts a single argument.\n";
970 static char usage_recvchar[] =
971 " Usage: RECEIVE CHAR <timeout>\n"
972 " Receives a character of text on a channel. Specify timeout to be the\n"
973 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
974 " do not support the reception of text. Returns the decimal value of the character\n"
975 " if one is received, or 0 if the channel does not support text reception. Returns\n"
976 " -1 only on error/hangup.\n";
978 static char usage_tddmode[] =
979 " Usage: TDD MODE <on|off>\n"
980 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
981 " successful, or 0 if channel is not TDD-capable.\n";
983 static char usage_sendimage[] =
984 " Usage: SEND IMAGE <image>\n"
985 " Sends the given image on a channel. Most channels do not support the\n"
986 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
987 " support image transmission. Returns -1 only on error/hangup. Image names\n"
988 " should not include extensions.\n";
990 static char usage_streamfile[] =
991 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
992 " Send the given file, allowing playback to be interrupted by the given\n"
993 " digits, if any. Use double quotes for the digits if you wish none to be\n"
994 " permitted. If sample offset is provided then the audio will seek to sample\n"
995 " offset before play starts. Returns 0 if playback completes without a digit\n"
996 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
997 " or -1 on error or if the channel was disconnected. Remember, the file\n"
998 " extension must not be included in the filename.\n";
1000 static char usage_saynumber[] =
1001 " Usage: SAY NUMBER <number> <escape digits>\n"
1002 " Say a given number, returning early if any of the given DTMF digits\n"
1003 " are received on the channel. Returns 0 if playback completes without a digit\n"
1004 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1005 " -1 on error/hangup.\n";
1007 static char usage_saydigits[] =
1008 " Usage: SAY DIGITS <number> <escape digits>\n"
1009 " Say a given digit string, returning early if any of the given DTMF digits\n"
1010 " are received on the channel. Returns 0 if playback completes without a digit\n"
1011 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1012 " -1 on error/hangup.\n";
1014 static char usage_saytime[] =
1015 " Usage: SAY TIME <time> <escape digits>\n"
1016 " Say a given time, returning early if any of the given DTMF digits are\n"
1017 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1018 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1019 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1020 " digit if one was pressed or -1 on error/hangup.\n";
1022 static char usage_getdata[] =
1023 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1024 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
1025 "from the channel at the other end.\n";
1027 static char usage_setcontext[] =
1028 " Usage: SET CONTEXT <desired context>\n"
1029 " Sets the context for continuation upon exiting the application.\n";
1031 static char usage_setextension[] =
1032 " Usage: SET EXTENSION <new extension>\n"
1033 " Changes the extension for continuation upon exiting the application.\n";
1035 static char usage_setpriority[] =
1036 " Usage: SET PRIORITY <num>\n"
1037 " Changes the priority for continuation upon exiting the application.\n";
1039 static char usage_recordfile[] =
1040 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1041 " Record to a file until a given dtmf digit in the sequence is received\n"
1042 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1043 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1044 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1045 " the offset without exceeding the end of the file. \"silence\" is the number\n"
1046 " of seconds of silence allowed before the function returns despite the\n"
1047 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1048 " preceeded by \"s=\" and is optional.\n";
1051 static char usage_autohangup[] =
1052 " Usage: SET AUTOHANGUP <time>\n"
1053 " Cause the channel to automatically hangup at <time> seconds in the\n"
1054 "future. Of course it can be hungup before then as well. Setting to\n"
1055 "0 will cause the autohangup feature to be disabled on this channel.\n";
1057 static char usage_noop[] =
1061 static agi_command commands[] = {
1062 { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1063 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1064 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1065 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1066 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1067 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1068 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1069 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1070 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1071 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1072 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1073 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1074 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1075 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1076 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1077 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1078 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1079 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1080 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1081 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1082 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1083 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1084 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1085 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1086 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1087 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1088 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1089 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1090 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1093 static void join(char *s, int len, char *w[])
1096 /* Join words into a string */
1098 for (x=0;w[x];x++) {
1100 strncat(s, " ", len - strlen(s));
1101 strncat(s, w[x], len - strlen(s));
1105 static int help_workhorse(int fd, char *match[])
1110 struct agi_command *e;
1112 join(matchstr, sizeof(matchstr), match);
1113 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1116 join(fullcmd, sizeof(fullcmd), e->cmda);
1117 /* Hide commands that start with '_' */
1118 if (fullcmd[0] == '_')
1121 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1125 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1130 static agi_command *find_command(char *cmds[], int exact)
1135 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1136 /* start optimistic */
1138 for (y=0;match && cmds[y]; y++) {
1139 /* If there are no more words in the command (and we're looking for
1140 an exact match) or there is a difference between the two words,
1141 then this is not a match */
1142 if (!commands[x].cmda[y] && !exact)
1144 /* don't segfault if the next part of a command doesn't exist */
1145 if (!commands[x].cmda[y]) return NULL;
1146 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1149 /* If more words are needed to complete the command then this is not
1150 a candidate (unless we're looking for a really inexact answer */
1151 if ((exact > -1) && commands[x].cmda[y])
1154 return &commands[x];
1160 static int parse_args(char *s, int *max, char *argv[])
1172 /* If it's escaped, put a literal quote */
1177 if (quoted && whitespace) {
1178 /* If we're starting a quote, coming off white space start a new word, too */
1186 if (!quoted && !escaped) {
1187 /* If we're not quoted, mark this as whitespace, and
1188 end the previous argument */
1192 /* Otherwise, just treat it as anything else */
1196 /* If we're escaped, print a literal, otherwise enable escaping */
1206 if (x >= MAX_ARGS -1) {
1207 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1210 /* Coming off of whitespace, start the next argument */
1219 /* Null terminate */
1226 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1228 char *argv[MAX_ARGS];
1233 parse_args(buf, &argc, argv);
1236 for (x=0;x<argc;x++)
1237 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1239 c = find_command(argv, 0);
1241 res = c->handler(chan, agi, argc, argv);
1243 case RESULT_SHOWUSAGE:
1244 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1245 fdprintf(agi->fd, c->usage);
1246 fdprintf(agi->fd, "520 End of proper usage.\n");
1248 case RESULT_FAILURE:
1249 /* They've already given the failure. We've been hung up on so handle this
1254 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1259 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid)
1261 struct ast_channel *c;
1264 int returnstatus = 0;
1265 struct ast_frame *f;
1268 //how many times we'll retry if ast_waitfor_nandfs will return without either channel or file descriptor in case select is interrupted by a system call (EINTR)
1271 if (!(readf = fdopen(agi->ctrl, "r"))) {
1272 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1277 setup_env(chan, request, agi->fd, (agi->audio > -1));
1280 c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1283 /* Idle the channel until we get a command */
1286 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1290 /* If it's voice, write it to the audio pipe */
1291 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1292 /* Write, ignoring errors */
1293 write(agi->audio, f->data, f->datalen);
1297 } else if (outfd > -1) {
1299 if (!fgets(buf, sizeof(buf), readf)) {
1300 /* Program terminated */
1303 if (option_verbose > 2)
1304 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1305 /* No need to kill the pid anymore, since they closed us */
1309 /* get rid of trailing newline, if any */
1310 if (*buf && buf[strlen(buf) - 1] == '\n')
1311 buf[strlen(buf) - 1] = 0;
1313 returnstatus |= agi_handle_command(chan, agi, buf);
1314 /* If the handle_command returns -1, we need to stop */
1315 if (returnstatus < 0) {
1320 ast_log(LOG_WARNING, "No channel, no fd?\n");
1326 /* Notify process */
1330 return returnstatus;
1333 static int handle_showagi(int fd, int argc, char *argv[]) {
1334 struct agi_command *e;
1337 return RESULT_SHOWUSAGE;
1339 e = find_command(argv + 2, 1);
1341 ast_cli(fd, e->usage);
1343 if (find_command(argv + 2, -1)) {
1344 return help_workhorse(fd, argv + 1);
1346 join(fullcmd, sizeof(fullcmd), argv+1);
1347 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1351 return help_workhorse(fd, NULL);
1353 return RESULT_SUCCESS;
1356 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1357 struct agi_command *e;
1364 return RESULT_SHOWUSAGE;
1366 if (!(htmlfile = fopen(argv[2], "wt"))) {
1367 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1368 return RESULT_SHOWUSAGE;
1371 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1372 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1375 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1377 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1381 join(fullcmd, sizeof(fullcmd), e->cmda);
1382 /* Hide commands that start with '_' */
1383 if (fullcmd[0] == '_')
1386 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1387 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1391 tempstr = strsep(&stringp, "\n");
1393 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1395 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1396 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1397 fprintf(htmlfile, "%s<BR>\n",tempstr);
1400 fprintf(htmlfile, "</TD></TR>\n");
1401 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1405 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1407 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1408 return RESULT_SUCCESS;
1411 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced)
1414 struct localuser *u;
1422 if (!data || !strlen(data)) {
1423 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1428 memset(&agi, 0, sizeof(agi));
1429 strncpy(tmp, data, sizeof(tmp)-1);
1430 strsep(&stringp, "|");
1431 args = strsep(&stringp, "|");
1432 ringy = strsep(&stringp,"|");
1437 /* Answer if need be */
1438 if (chan->_state != AST_STATE_UP) {
1439 if (ringy) { /* if for ringing first */
1440 /* a little ringy-dingy first */
1441 ast_indicate(chan, AST_CONTROL_RINGING);
1444 if (ast_answer(chan)) {
1445 LOCAL_USER_REMOVE(u);
1450 res = launch_script(tmp, args, fds, enhanced ? &efd : NULL, &pid);
1455 res = run_agi(chan, tmp, &agi, pid);
1461 LOCAL_USER_REMOVE(u);
1465 static int agi_exec(struct ast_channel *chan, void *data)
1467 return agi_exec_full(chan, data, 0);
1470 static int eagi_exec(struct ast_channel *chan, void *data)
1474 readformat = chan->readformat;
1475 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1476 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1479 res = agi_exec_full(chan, data, 1);
1481 if (ast_set_read_format(chan, readformat)) {
1482 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1488 static char showagi_help[] =
1489 "Usage: show agi [topic]\n"
1490 " When called with a topic as an argument, displays usage\n"
1491 " information on the given command. If called without a\n"
1492 " topic, it provides a list of AGI commands.\n";
1495 static char dumpagihtml_help[] =
1496 "Usage: dump agihtml <filename>\n"
1497 " Dumps the agi command list in html format to given filename\n";
1499 static struct ast_cli_entry showagi =
1500 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1502 static struct ast_cli_entry dumpagihtml =
1503 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1505 int unload_module(void)
1507 STANDARD_HANGUP_LOCALUSERS;
1508 ast_cli_unregister(&showagi);
1509 ast_cli_unregister(&dumpagihtml);
1510 ast_unregister_application(eapp);
1511 return ast_unregister_application(app);
1514 int load_module(void)
1516 ast_cli_register(&showagi);
1517 ast_cli_register(&dumpagihtml);
1518 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1519 return ast_register_application(app, agi_exec, synopsis, descrip);
1522 char *description(void)
1530 STANDARD_USECOUNT(res);
1536 return ASTERISK_GPL_KEY;