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_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
407 return RESULT_SHOWUSAGE;
408 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
409 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
410 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
411 if (res == 2) /* New command */
412 return RESULT_SUCCESS;
414 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
416 fdprintf(agi->fd, "200 result=-1\n");
418 fdprintf(agi->fd, "200 result=%s\n", data);
420 return RESULT_SUCCESS;
422 return RESULT_FAILURE;
425 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
429 return RESULT_SHOWUSAGE;
430 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
431 fdprintf(agi->fd, "200 result=0\n");
432 return RESULT_SUCCESS;
435 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
438 return RESULT_SHOWUSAGE;
439 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
440 fdprintf(agi->fd, "200 result=0\n");
441 return RESULT_SUCCESS;
444 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
448 return RESULT_SHOWUSAGE;
449 if (sscanf(argv[2], "%i", &pri) != 1)
450 return RESULT_SHOWUSAGE;
451 chan->priority = pri - 1;
452 fdprintf(agi->fd, "200 result=0\n");
453 return RESULT_SUCCESS;
456 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
458 struct ast_filestream *fs;
460 struct timeval tv, start;
461 long sample_offset = 0;
465 struct ast_dsp *sildet=NULL; /* silence detector dsp */
466 int totalsilence = 0;
468 int silence = 0; /* amount of silence to allow */
469 int gotsilence = 0; /* did we timeout for silence? */
470 char *silencestr=NULL;
474 /* XXX EAGI FIXME XXX */
477 return RESULT_SHOWUSAGE;
478 if (sscanf(argv[5], "%i", &ms) != 1)
479 return RESULT_SHOWUSAGE;
482 silencestr = strchr(argv[6],'s');
483 if ((argc > 7) && (!silencestr))
484 silencestr = strchr(argv[7],'s');
485 if ((argc > 8) && (!silencestr))
486 silencestr = strchr(argv[8],'s');
489 if (strlen(silencestr) > 2) {
490 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
494 silence = atoi(silencestr);
502 rfmt = chan->readformat;
503 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
505 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
508 sildet = ast_dsp_new();
510 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
513 ast_dsp_set_threshold(sildet, 256);
516 /* backward compatibility, if no offset given, arg[6] would have been
517 * caught below and taken to be a beep, else if it is a digit then it is a
519 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
520 res = ast_streamfile(chan, "beep", chan->language);
522 if ((argc > 7) && (!strchr(argv[7], '=')))
523 res = ast_streamfile(chan, "beep", chan->language);
526 res = ast_waitstream(chan, argv[4]);
528 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
531 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
532 return RESULT_FAILURE;
536 ast_applystream(chan,fs);
537 /* really should have checks */
538 ast_seekstream(fs, sample_offset, SEEK_SET);
541 gettimeofday(&start, NULL);
542 gettimeofday(&tv, NULL);
543 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
544 res = ast_waitfor(chan, -1);
547 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
548 return RESULT_FAILURE;
552 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
554 return RESULT_FAILURE;
556 switch(f->frametype) {
558 if (strchr(argv[4], f->subclass)) {
559 /* This is an interrupting chracter */
560 sample_offset = ast_tellstream(fs);
561 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
564 return RESULT_SUCCESS;
567 case AST_FRAME_VOICE:
568 ast_writestream(fs, f);
569 /* this is a safe place to check progress since we know that fs
570 * is valid after a write, and it will then have our current
572 sample_offset = ast_tellstream(fs);
575 ast_dsp_silence(sildet, f, &dspsilence);
577 totalsilence = dspsilence;
581 if (totalsilence > silence) {
582 /* Ended happily with silence */
591 gettimeofday(&tv, NULL);
597 ast_stream_rewind(fs, silence-1000);
600 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
603 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
606 res = ast_set_read_format(chan, rfmt);
608 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
609 ast_dsp_free(sildet);
611 return RESULT_SUCCESS;
614 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
619 return RESULT_SHOWUSAGE;
620 if (sscanf(argv[2], "%d", &timeout) != 1)
621 return RESULT_SHOWUSAGE;
625 chan->whentohangup = time(NULL) + timeout;
627 chan->whentohangup = 0;
628 fdprintf(agi->fd, "200 result=0\n");
629 return RESULT_SUCCESS;
632 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
634 struct ast_channel *c;
636 /* no argument: hangup the current channel */
637 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
638 fdprintf(agi->fd, "200 result=1\n");
639 return RESULT_SUCCESS;
640 } else if (argc==2) {
641 /* one argument: look for info on the specified channel */
642 c = ast_channel_walk(NULL);
644 if (strcasecmp(argv[1],c->name)==0) {
645 /* we have a matching channel */
646 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
647 fdprintf(agi->fd, "200 result=1\n");
648 return RESULT_SUCCESS;
650 c = ast_channel_walk(c);
652 /* if we get this far no channel name matched the argument given */
653 fdprintf(agi->fd, "200 result=-1\n");
654 return RESULT_SUCCESS;
656 return RESULT_SHOWUSAGE;
660 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
666 return RESULT_SHOWUSAGE;
668 if (option_verbose > 2)
669 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
671 app = pbx_findapp(argv[1]);
674 res = pbx_exec(chan, app, argv[2], 1);
676 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
679 fdprintf(agi->fd, "200 result=%d\n", res);
684 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
687 ast_set_callerid(chan, argv[2], 0);
689 /* strncpy(chan->callerid, argv[2], sizeof(chan->callerid)-1);
690 */ fdprintf(agi->fd, "200 result=1\n");
691 return RESULT_SUCCESS;
694 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
696 struct ast_channel *c;
698 /* no argument: supply info on the current channel */
699 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
700 return RESULT_SUCCESS;
701 } else if (argc==3) {
702 /* one argument: look for info on the specified channel */
703 c = ast_channel_walk(NULL);
705 if (strcasecmp(argv[2],c->name)==0) {
706 fdprintf(agi->fd, "200 result=%d\n", c->_state);
707 return RESULT_SUCCESS;
709 c = ast_channel_walk(c);
711 /* if we get this far no channel name matched the argument given */
712 fdprintf(agi->fd, "200 result=-1\n");
713 return RESULT_SUCCESS;
715 return RESULT_SHOWUSAGE;
719 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
722 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
724 fdprintf(agi->fd, "200 result=1\n");
725 return RESULT_SUCCESS;
728 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
732 if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])) )
733 fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
735 fdprintf(agi->fd, "200 result=0\n");
737 return RESULT_SUCCESS;
740 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
746 return RESULT_SHOWUSAGE;
749 sscanf(argv[2], "%d", &level);
753 prefix = VERBOSE_PREFIX_4;
756 prefix = VERBOSE_PREFIX_3;
759 prefix = VERBOSE_PREFIX_2;
763 prefix = VERBOSE_PREFIX_1;
767 if (level <= option_verbose)
768 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
770 fdprintf(agi->fd, "200 result=1\n");
772 return RESULT_SUCCESS;
775 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
780 return RESULT_SHOWUSAGE;
781 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
783 fdprintf(agi->fd, "200 result=0\n");
785 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
787 return RESULT_SUCCESS;
790 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
794 return RESULT_SHOWUSAGE;
795 res = ast_db_put(argv[2], argv[3], argv[4]);
797 fdprintf(agi->fd, "200 result=0\n");
799 fdprintf(agi->fd, "200 result=1\n");
801 return RESULT_SUCCESS;
804 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
808 return RESULT_SHOWUSAGE;
809 res = ast_db_del(argv[2], argv[3]);
811 fdprintf(agi->fd, "200 result=0\n");
813 fdprintf(agi->fd, "200 result=1\n");
815 return RESULT_SUCCESS;
818 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
821 if ((argc < 3) || (argc > 4))
822 return RESULT_SHOWUSAGE;
824 res = ast_db_deltree(argv[2], argv[3]);
826 res = ast_db_deltree(argv[2], NULL);
829 fdprintf(agi->fd, "200 result=0\n");
831 fdprintf(agi->fd, "200 result=1\n");
832 return RESULT_SUCCESS;
835 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
837 fdprintf(agi->fd, "200 result=0\n");
838 return RESULT_SUCCESS;
841 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
843 if (!strncasecmp(argv[2],"on",2)) {
845 ast_moh_start(chan, argv[3]);
847 ast_moh_start(chan, NULL);
849 if (!strncasecmp(argv[2],"off",3)) {
852 fdprintf(agi->fd, "200 result=0\n");
853 return RESULT_SUCCESS;
856 static char usage_setmusic[] =
857 " Usage: SET MUSIC ON <on|off> <class>\n"
858 " Enables/Disables the music on hold generator. If <class> is\n"
859 " not specified then the default music on hold class will be used.\n"
860 " Always returns 0\n";
862 static char usage_dbput[] =
863 " Usage: DATABASE PUT <family> <key> <value>\n"
864 " Adds or updates an entry in the Asterisk database for a\n"
865 " given family, key, and value.\n"
866 " Returns 1 if succesful, 0 otherwise\n";
868 static char usage_dbget[] =
869 " Usage: DATABASE GET <family> <key>\n"
870 " Retrieves an entry in the Asterisk database for a\n"
871 " given family and key.\n"
872 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
873 " is set and returns the variable in parenthesis\n"
874 " example return code: 200 result=1 (testvariable)\n";
876 static char usage_dbdel[] =
877 " Usage: DATABASE DEL <family> <key>\n"
878 " Deletes an entry in the Asterisk database for a\n"
879 " given family and key.\n"
880 " Returns 1 if succesful, 0 otherwise\n";
882 static char usage_dbdeltree[] =
883 " Usage: DATABASE DELTREE <family> [keytree]\n"
884 " Deletes a family or specific keytree withing a family\n"
885 " in the Asterisk database.\n"
886 " Returns 1 if succesful, 0 otherwise\n";
888 static char usage_verbose[] =
889 " Usage: VERBOSE <message> <level>\n"
890 " Sends <message> to the console via verbose message system.\n"
891 " <level> is the the verbose level (1-4)\n"
892 " Always returns 1\n";
894 static char usage_getvariable[] =
895 " Usage: GET VARIABLE <variablename>\n"
896 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
897 " is set and returns the variable in parenthesis\n"
898 " example return code: 200 result=1 (testvariable)\n";
900 static char usage_setvariable[] =
901 " Usage: SET VARIABLE <variablename> <value>\n";
903 static char usage_channelstatus[] =
904 " Usage: CHANNEL STATUS [<channelname>]\n"
905 " Returns the status of the specified channel.\n"
906 " If no channel name is given the returns the status of the\n"
907 " current channel.\n"
909 " 0 Channel is down and available\n"
910 " 1 Channel is down, but reserved\n"
911 " 2 Channel is off hook\n"
912 " 3 Digits (or equivalent) have been dialed\n"
913 " 4 Line is ringing\n"
914 " 5 Remote end is ringing\n"
918 static char usage_setcallerid[] =
919 " Usage: SET CALLERID <number>\n"
920 " Changes the callerid of the current channel.\n";
922 static char usage_exec[] =
923 " Usage: EXEC <application> <options>\n"
924 " Executes <application> with given <options>.\n"
925 " Returns whatever the application returns, or -2 on failure to find application\n";
927 static char usage_hangup[] =
928 " Usage: HANGUP [<channelname>]\n"
929 " Hangs up the specified channel.\n"
930 " If no channel name is given, hangs up the current channel\n";
932 static char usage_answer[] =
934 " Answers channel if not already in answer state. Returns -1 on\n"
935 " channel failure, or 0 if successful.\n";
937 static char usage_waitfordigit[] =
938 " Usage: WAIT FOR DIGIT <timeout>\n"
939 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
940 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
941 " the numerical value of the ascii of the digit if one is received. Use -1\n"
942 " for the timeout value if you desire the call to block indefinitely.\n";
944 static char usage_sendtext[] =
945 " Usage: SEND TEXT \"<text to send>\"\n"
946 " Sends the given text on a channel. Most channels do not support the\n"
947 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
948 " support text transmission. Returns -1 only on error/hangup. Text\n"
949 " consisting of greater than one word should be placed in quotes since the\n"
950 " command only accepts a single argument.\n";
952 static char usage_recvchar[] =
953 " Usage: RECEIVE CHAR <timeout>\n"
954 " Receives a character of text on a channel. Specify timeout to be the\n"
955 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
956 " do not support the reception of text. Returns the decimal value of the character\n"
957 " if one is received, or 0 if the channel does not support text reception. Returns\n"
958 " -1 only on error/hangup.\n";
960 static char usage_tddmode[] =
961 " Usage: TDD MODE <on|off>\n"
962 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
963 " successful, or 0 if channel is not TDD-capable.\n";
965 static char usage_sendimage[] =
966 " Usage: SEND IMAGE <image>\n"
967 " Sends the given image on a channel. Most channels do not support the\n"
968 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
969 " support image transmission. Returns -1 only on error/hangup. Image names\n"
970 " should not include extensions.\n";
972 static char usage_streamfile[] =
973 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
974 " Send the given file, allowing playback to be interrupted by the given\n"
975 " digits, if any. Use double quotes for the digits if you wish none to be\n"
976 " permitted. If sample offset is provided then the audio will seek to sample\n"
977 " offset before play starts. Returns 0 if playback completes without a digit\n"
978 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
979 " or -1 on error or if the channel was disconnected. Remember, the file\n"
980 " extension must not be included in the filename.\n";
982 static char usage_saynumber[] =
983 " Usage: SAY NUMBER <number> <escape digits>\n"
984 " Say a given number, returning early if any of the given DTMF digits\n"
985 " are received on the channel. Returns 0 if playback completes without a digit\n"
986 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
987 " -1 on error/hangup.\n";
989 static char usage_saydigits[] =
990 " Usage: SAY DIGITS <number> <escape digits>\n"
991 " Say a given digit string, returning early if any of the given DTMF digits\n"
992 " are received on the channel. Returns 0 if playback completes without a digit\n"
993 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
994 " -1 on error/hangup.\n";
996 static char usage_getdata[] =
997 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
998 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
999 "from the channel at the other end.\n";
1001 static char usage_setcontext[] =
1002 " Usage: SET CONTEXT <desired context>\n"
1003 " Sets the context for continuation upon exiting the application.\n";
1005 static char usage_setextension[] =
1006 " Usage: SET EXTENSION <new extension>\n"
1007 " Changes the extension for continuation upon exiting the application.\n";
1009 static char usage_setpriority[] =
1010 " Usage: SET PRIORITY <num>\n"
1011 " Changes the priority for continuation upon exiting the application.\n";
1013 static char usage_recordfile[] =
1014 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1015 " Record to a file until a given dtmf digit in the sequence is received\n"
1016 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1017 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1018 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1019 " the offset without exceeding the end of the file. \"silence\" is the number\n"
1020 " of seconds of silence allowed before the function returns despite the\n"
1021 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1022 " preceeded by \"s=\" and is optional.\n";
1025 static char usage_autohangup[] =
1026 " Usage: SET AUTOHANGUP <time>\n"
1027 " Cause the channel to automatically hangup at <time> seconds in the\n"
1028 "future. Of course it can be hungup before then as well. Setting to\n"
1029 "0 will cause the autohangup feature to be disabled on this channel.\n";
1031 static char usage_noop[] =
1035 static agi_command commands[] = {
1036 { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1037 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1038 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1039 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1040 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1041 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1042 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1043 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1044 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1045 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1046 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1047 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1048 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1049 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1050 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1051 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1052 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1053 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1054 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1055 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1056 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1057 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1058 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1059 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1060 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1061 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1062 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1063 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1066 static void join(char *s, int len, char *w[])
1069 /* Join words into a string */
1071 for (x=0;w[x];x++) {
1073 strncat(s, " ", len - strlen(s));
1074 strncat(s, w[x], len - strlen(s));
1078 static int help_workhorse(int fd, char *match[])
1083 struct agi_command *e;
1085 join(matchstr, sizeof(matchstr), match);
1086 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1089 join(fullcmd, sizeof(fullcmd), e->cmda);
1090 /* Hide commands that start with '_' */
1091 if (fullcmd[0] == '_')
1094 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1098 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1103 static agi_command *find_command(char *cmds[], int exact)
1108 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1109 /* start optimistic */
1111 for (y=0;match && cmds[y]; y++) {
1112 /* If there are no more words in the command (and we're looking for
1113 an exact match) or there is a difference between the two words,
1114 then this is not a match */
1115 if (!commands[x].cmda[y] && !exact)
1117 /* don't segfault if the next part of a command doesn't exist */
1118 if (!commands[x].cmda[y]) return NULL;
1119 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1122 /* If more words are needed to complete the command then this is not
1123 a candidate (unless we're looking for a really inexact answer */
1124 if ((exact > -1) && commands[x].cmda[y])
1127 return &commands[x];
1133 static int parse_args(char *s, int *max, char *argv[])
1145 /* If it's escaped, put a literal quote */
1150 if (quoted && whitespace) {
1151 /* If we're starting a quote, coming off white space start a new word, too */
1159 if (!quoted && !escaped) {
1160 /* If we're not quoted, mark this as whitespace, and
1161 end the previous argument */
1165 /* Otherwise, just treat it as anything else */
1169 /* If we're escaped, print a literal, otherwise enable escaping */
1179 if (x >= MAX_ARGS -1) {
1180 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1183 /* Coming off of whitespace, start the next argument */
1192 /* Null terminate */
1199 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1201 char *argv[MAX_ARGS];
1206 parse_args(buf, &argc, argv);
1209 for (x=0;x<argc;x++)
1210 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1212 c = find_command(argv, 0);
1214 res = c->handler(chan, agi, argc, argv);
1216 case RESULT_SHOWUSAGE:
1217 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1218 fdprintf(agi->fd, c->usage);
1219 fdprintf(agi->fd, "520 End of proper usage.\n");
1221 case RESULT_FAILURE:
1222 /* They've already given the failure. We've been hung up on so handle this
1227 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1232 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid)
1234 struct ast_channel *c;
1237 int returnstatus = 0;
1238 struct ast_frame *f;
1241 //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)
1244 if (!(readf = fdopen(agi->ctrl, "r"))) {
1245 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1250 setup_env(chan, request, agi->fd, (agi->audio > -1));
1253 c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1256 /* Idle the channel until we get a command */
1259 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1263 /* If it's voice, write it to the audio pipe */
1264 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1265 /* Write, ignoring errors */
1266 write(agi->audio, f->data, f->datalen);
1270 } else if (outfd > -1) {
1272 if (!fgets(buf, sizeof(buf), readf)) {
1273 /* Program terminated */
1276 if (option_verbose > 2)
1277 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1278 /* No need to kill the pid anymore, since they closed us */
1282 /* get rid of trailing newline, if any */
1283 if (*buf && buf[strlen(buf) - 1] == '\n')
1284 buf[strlen(buf) - 1] = 0;
1286 returnstatus |= agi_handle_command(chan, agi, buf);
1287 /* If the handle_command returns -1, we need to stop */
1288 if (returnstatus < 0) {
1293 ast_log(LOG_WARNING, "No channel, no fd?\n");
1299 /* Notify process */
1303 return returnstatus;
1306 static int handle_showagi(int fd, int argc, char *argv[]) {
1307 struct agi_command *e;
1310 return RESULT_SHOWUSAGE;
1312 e = find_command(argv + 2, 1);
1314 ast_cli(fd, e->usage);
1316 if (find_command(argv + 2, -1)) {
1317 return help_workhorse(fd, argv + 1);
1319 join(fullcmd, sizeof(fullcmd), argv+1);
1320 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1324 return help_workhorse(fd, NULL);
1326 return RESULT_SUCCESS;
1329 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1330 struct agi_command *e;
1337 return RESULT_SHOWUSAGE;
1339 if (!(htmlfile = fopen(argv[2], "wt"))) {
1340 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1341 return RESULT_SHOWUSAGE;
1344 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1345 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1348 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1350 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1354 join(fullcmd, sizeof(fullcmd), e->cmda);
1355 /* Hide commands that start with '_' */
1356 if (fullcmd[0] == '_')
1359 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1360 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1364 tempstr = strsep(&stringp, "\n");
1366 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1368 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1369 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1370 fprintf(htmlfile, "%s<BR>\n",tempstr);
1373 fprintf(htmlfile, "</TD></TR>\n");
1374 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1378 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1380 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1381 return RESULT_SUCCESS;
1384 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced)
1387 struct localuser *u;
1395 if (!data || !strlen(data)) {
1396 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1401 memset(&agi, 0, sizeof(agi));
1402 strncpy(tmp, data, sizeof(tmp)-1);
1403 strsep(&stringp, "|");
1404 args = strsep(&stringp, "|");
1405 ringy = strsep(&stringp,"|");
1410 /* Answer if need be */
1411 if (chan->_state != AST_STATE_UP) {
1412 if (ringy) { /* if for ringing first */
1413 /* a little ringy-dingy first */
1414 ast_indicate(chan, AST_CONTROL_RINGING);
1417 if (ast_answer(chan)) {
1418 LOCAL_USER_REMOVE(u);
1423 res = launch_script(tmp, args, fds, enhanced ? &efd : NULL, &pid);
1428 res = run_agi(chan, tmp, &agi, pid);
1434 LOCAL_USER_REMOVE(u);
1438 static int agi_exec(struct ast_channel *chan, void *data)
1440 return agi_exec_full(chan, data, 0);
1443 static int eagi_exec(struct ast_channel *chan, void *data)
1447 readformat = chan->readformat;
1448 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1449 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1452 res = agi_exec_full(chan, data, 1);
1454 if (ast_set_read_format(chan, readformat)) {
1455 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1461 static char showagi_help[] =
1462 "Usage: show agi [topic]\n"
1463 " When called with a topic as an argument, displays usage\n"
1464 " information on the given command. If called without a\n"
1465 " topic, it provides a list of AGI commands.\n";
1468 static char dumpagihtml_help[] =
1469 "Usage: dump agihtml <filename>\n"
1470 " Dumps the agi command list in html format to given filename\n";
1472 static struct ast_cli_entry showagi =
1473 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1475 static struct ast_cli_entry dumpagihtml =
1476 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1478 int unload_module(void)
1480 STANDARD_HANGUP_LOCALUSERS;
1481 ast_cli_unregister(&showagi);
1482 ast_cli_unregister(&dumpagihtml);
1483 ast_unregister_application(eapp);
1484 return ast_unregister_application(app);
1487 int load_module(void)
1489 ast_cli_register(&showagi);
1490 ast_cli_register(&dumpagihtml);
1491 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1492 return ast_register_application(app, agi_exec, synopsis, descrip);
1495 char *description(void)
1503 STANDARD_USECOUNT(res);
1509 return ASTERISK_GPL_KEY;