2 * Asterisk -- A telephony toolkit for Linux.
4 * Asterisk Gateway Interface
6 * Copyright (C) 1999-2004, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <netinet/tcp.h>
19 #include <arpa/inet.h>
20 #include <asterisk/file.h>
21 #include <asterisk/logger.h>
22 #include <asterisk/channel.h>
23 #include <asterisk/pbx.h>
24 #include <asterisk/module.h>
25 #include <asterisk/astdb.h>
26 #include <asterisk/callerid.h>
37 #include <asterisk/cli.h>
38 #include <asterisk/logger.h>
39 #include <asterisk/options.h>
40 #include <asterisk/image.h>
41 #include <asterisk/say.h>
42 #include <asterisk/app.h>
43 #include <asterisk/dsp.h>
44 #include <asterisk/musiconhold.h>
45 #include <asterisk/manager.h>
46 #include <asterisk/utils.h>
47 #include <asterisk/lock.h>
48 #include <asterisk/agi.h>
49 #include "../asterisk.h"
50 #include "../astconf.h"
53 #define MAX_COMMANDS 128
55 /* Recycle some stuff from the CLI interface */
56 #define fdprintf agi_debug_cli
58 static char *tdesc = "Asterisk Gateway Interface (AGI)";
60 static char *app = "AGI";
62 static char *eapp = "EAGI";
64 static char *deadapp = "DeadAGI";
66 static char *synopsis = "Executes an AGI compliant application";
67 static char *esynopsis = "Executes an EAGI compliant application";
68 static char *deadsynopsis = "Executes AGI on a hungup channel";
70 static char *descrip =
71 " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
72 "program on a channel. AGI allows Asterisk to launch external programs\n"
73 "written in any language to control a telephony channel, play audio,\n"
74 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
76 "Returns -1 on hangup (except for DeadAGI) or if application requested\n"
77 " hangup, or 0 on non-hangup exit. \n"
78 "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
79 "on file descriptor 3\n\n"
80 "Use the CLI command 'show agi' to list available agi commands\n";
82 static int agidebug = 0;
89 #define TONE_BLOCK_SIZE 200
91 /* Max time to connect to an AGI remote host */
92 #define MAX_AGI_CONNECT 2000
96 static void agi_debug_cli(int fd, char *fmt, ...)
103 res = vasprintf(&stuff, fmt, ap);
106 ast_log(LOG_ERROR, "Out of memory\n");
109 ast_verbose("AGI Tx >> %s", stuff);
110 ast_carefulwrite(fd, stuff, strlen(stuff), 100);
115 static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
119 struct pollfd pfds[1];
121 char *c; int port = AGI_PORT;
123 struct sockaddr_in sin;
125 struct ast_hostent ahp;
126 ast_log(LOG_DEBUG, "Blah\n");
127 host = ast_strdupa(agiurl + 6);
130 /* Strip off any script name */
131 if ((c = strchr(host, '/'))) {
136 if ((c = strchr(host, ':'))) {
142 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
145 hp = ast_gethostbyname(host, &ahp);
147 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
150 s = socket(AF_INET, SOCK_STREAM, 0);
152 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
155 flags = fcntl(s, F_GETFL);
157 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
161 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
162 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
166 memset(&sin, 0, sizeof(sin));
167 sin.sin_family = AF_INET;
168 sin.sin_port = htons(port);
169 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
170 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) {
171 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
176 pfds[0].events = POLLOUT;
177 if (poll(pfds, 1, MAX_AGI_CONNECT) != 1) {
178 ast_log(LOG_WARNING, "Connect to '%s' failed!\n", agiurl);
182 if (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) {
183 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
187 ast_log(LOG_DEBUG, "Wow, connected!\n");
194 static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
204 if (!strncasecmp(script, "agi://", 6))
205 return launch_netscript(script, argv, fds, efd, opid);
207 if (script[0] != '/') {
208 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
212 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
216 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
223 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
230 res = fcntl(audio[1], F_GETFL);
232 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
234 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
246 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
250 /* Redirect stdin and out, provide enhanced audio channel if desired */
251 dup2(fromast[0], STDIN_FILENO);
252 dup2(toast[1], STDOUT_FILENO);
254 dup2(audio[0], STDERR_FILENO + 1);
256 close(STDERR_FILENO + 1);
258 /* Close everything but stdin/out/error */
259 for (x=STDERR_FILENO + 2;x<1024;x++)
263 /* Can't use ast_log since FD's are closed */
264 fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
267 if (option_verbose > 2)
268 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
274 /* close what we're not using in the parent */
288 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
290 /* Print initial environment, with agi_request always being the first
292 fdprintf(fd, "agi_request: %s\n", request);
293 fdprintf(fd, "agi_channel: %s\n", chan->name);
294 fdprintf(fd, "agi_language: %s\n", chan->language);
295 fdprintf(fd, "agi_type: %s\n", chan->type);
296 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
299 fdprintf(fd, "agi_callerid: %s\n", chan->cid.cid_num ? chan->cid.cid_num : "unknown");
300 fdprintf(fd, "agi_calleridname: %s\n", chan->cid.cid_name ? chan->cid.cid_name : "unknown");
301 fdprintf(fd, "agi_dnid: %s\n", chan->cid.cid_dnid ? chan->cid.cid_dnid : "unknown");
302 fdprintf(fd, "agi_rdnis: %s\n", chan->cid.cid_rdnis ? chan->cid.cid_rdnis : "unknown");
304 /* Context information */
305 fdprintf(fd, "agi_context: %s\n", chan->context);
306 fdprintf(fd, "agi_extension: %s\n", chan->exten);
307 fdprintf(fd, "agi_priority: %d\n", chan->priority);
308 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
310 /* User information */
311 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
313 /* End with empty return */
317 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
321 if (chan->_state != AST_STATE_UP) {
322 /* Answer the chan */
323 res = ast_answer(chan);
325 fdprintf(agi->fd, "200 result=%d\n", res);
327 return RESULT_SUCCESS;
329 return RESULT_FAILURE;
332 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
337 return RESULT_SHOWUSAGE;
338 if (sscanf(argv[3], "%i", &to) != 1)
339 return RESULT_SHOWUSAGE;
340 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
341 fdprintf(agi->fd, "200 result=%d\n", res);
343 return RESULT_SUCCESS;
345 return RESULT_FAILURE;
348 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
352 return RESULT_SHOWUSAGE;
353 /* At the moment, the parser (perhaps broken) returns with
354 the last argument PLUS the newline at the end of the input
355 buffer. This probably needs to be fixed, but I wont do that
356 because other stuff may break as a result. The right way
357 would probably be to strip off the trailing newline before
358 parsing, then here, add a newline at the end of the string
359 before sending it to ast_sendtext --DUDE */
360 res = ast_sendtext(chan, argv[2]);
361 fdprintf(agi->fd, "200 result=%d\n", res);
363 return RESULT_SUCCESS;
365 return RESULT_FAILURE;
368 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
372 return RESULT_SHOWUSAGE;
373 res = ast_recvchar(chan,atoi(argv[2]));
375 fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
376 return RESULT_SUCCESS;
379 fdprintf(agi->fd, "200 result=%d\n", res);
380 return RESULT_SUCCESS;
383 fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
384 return RESULT_FAILURE;
388 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
392 return RESULT_SHOWUSAGE;
393 if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
394 if (!strncasecmp(argv[2],"mate",4)) x = 2;
395 if (!strncasecmp(argv[2],"tdd",3)) x = 1;
396 res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
397 fdprintf(agi->fd, "200 result=%d\n", res);
399 return RESULT_SUCCESS;
401 return RESULT_FAILURE;
404 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
408 return RESULT_SHOWUSAGE;
409 res = ast_send_image(chan, argv[2]);
410 if (!ast_check_hangup(chan))
412 fdprintf(agi->fd, "200 result=%d\n", res);
414 return RESULT_SUCCESS;
416 return RESULT_FAILURE;
419 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
422 struct ast_filestream *fs;
423 long sample_offset = 0;
427 return RESULT_SHOWUSAGE;
429 return RESULT_SHOWUSAGE;
430 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
431 return RESULT_SHOWUSAGE;
433 fs = ast_openstream(chan, argv[2], chan->language);
435 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
436 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
437 return RESULT_FAILURE;
439 ast_seekstream(fs, 0, SEEK_END);
440 max_length = ast_tellstream(fs);
441 ast_seekstream(fs, sample_offset, SEEK_SET);
442 res = ast_applystream(chan, fs);
443 res = ast_playstream(fs);
445 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
447 return RESULT_SHOWUSAGE;
449 return RESULT_FAILURE;
451 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
452 /* this is to check for if ast_waitstream closed the stream, we probably are at
453 * the end of the stream, return that amount, else check for the amount */
454 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
455 ast_stopstream(chan);
457 /* Stop this command, don't print a result line, as there is a new command */
458 return RESULT_SUCCESS;
460 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
462 return RESULT_SUCCESS;
464 return RESULT_FAILURE;
467 /* get option - really similar to the handle_streamfile, but with a timeout */
468 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
471 struct ast_filestream *fs;
472 long sample_offset = 0;
475 char *edigits = NULL;
477 if ( argc < 4 || argc > 5 )
478 return RESULT_SHOWUSAGE;
484 timeout = atoi(argv[4]);
485 else if (chan->pbx->dtimeout) {
486 /* by default dtimeout is set to 5sec */
487 timeout = chan->pbx->dtimeout * 1000; /* in msec */
490 fs = ast_openstream(chan, argv[2], chan->language);
492 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
493 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
494 return RESULT_FAILURE;
496 if (option_verbose > 2)
497 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
499 ast_seekstream(fs, 0, SEEK_END);
500 max_length = ast_tellstream(fs);
501 ast_seekstream(fs, sample_offset, SEEK_SET);
502 res = ast_applystream(chan, fs);
503 res = ast_playstream(fs);
505 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
507 return RESULT_SHOWUSAGE;
509 return RESULT_FAILURE;
511 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
512 /* this is to check for if ast_waitstream closed the stream, we probably are at
513 * the end of the stream, return that amount, else check for the amount */
514 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
515 ast_stopstream(chan);
517 /* Stop this command, don't print a result line, as there is a new command */
518 return RESULT_SUCCESS;
521 /* If the user didnt press a key, wait for digitTimeout*/
523 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
524 /* Make sure the new result is in the escape digits of the GET OPTION */
525 if ( !strchr(edigits,res) )
529 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
531 return RESULT_SUCCESS;
533 return RESULT_FAILURE;
539 /*--- handle_saynumber: Say number in various language syntaxes ---*/
540 /* Need to add option for gender here as well. Coders wanted */
541 /* While waiting, we're sending a (char *) NULL. */
542 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
547 return RESULT_SHOWUSAGE;
548 if (sscanf(argv[2], "%i", &num) != 1)
549 return RESULT_SHOWUSAGE;
550 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
552 return RESULT_SUCCESS;
553 fdprintf(agi->fd, "200 result=%d\n", res);
555 return RESULT_SUCCESS;
557 return RESULT_FAILURE;
560 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
566 return RESULT_SHOWUSAGE;
567 if (sscanf(argv[2], "%i", &num) != 1)
568 return RESULT_SHOWUSAGE;
570 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
571 if (res == 1) /* New command */
572 return RESULT_SUCCESS;
573 fdprintf(agi->fd, "200 result=%d\n", res);
575 return RESULT_SUCCESS;
577 return RESULT_FAILURE;
580 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
585 return RESULT_SHOWUSAGE;
587 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
588 if (res == 1) /* New command */
589 return RESULT_SUCCESS;
590 fdprintf(agi->fd, "200 result=%d\n", res);
592 return RESULT_SUCCESS;
594 return RESULT_FAILURE;
597 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
602 return RESULT_SHOWUSAGE;
603 if (sscanf(argv[2], "%i", &num) != 1)
604 return RESULT_SHOWUSAGE;
605 res = ast_say_time(chan, num, argv[3], chan->language);
607 return RESULT_SUCCESS;
608 fdprintf(agi->fd, "200 result=%d\n", res);
610 return RESULT_SUCCESS;
612 return RESULT_FAILURE;
615 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
620 return RESULT_SHOWUSAGE;
622 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
623 if (res == 1) /* New command */
624 return RESULT_SUCCESS;
625 fdprintf(agi->fd, "200 result=%d\n", res);
627 return RESULT_SUCCESS;
629 return RESULT_FAILURE;
632 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
640 return RESULT_SHOWUSAGE;
641 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
642 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
643 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
644 if (res == 2) /* New command */
645 return RESULT_SUCCESS;
647 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
649 fdprintf(agi->fd, "200 result=-1\n");
651 fdprintf(agi->fd, "200 result=%s\n", data);
653 return RESULT_SUCCESS;
655 return RESULT_FAILURE;
658 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
662 return RESULT_SHOWUSAGE;
663 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
664 fdprintf(agi->fd, "200 result=0\n");
665 return RESULT_SUCCESS;
668 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
671 return RESULT_SHOWUSAGE;
672 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
673 fdprintf(agi->fd, "200 result=0\n");
674 return RESULT_SUCCESS;
677 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
681 return RESULT_SHOWUSAGE;
682 if (sscanf(argv[2], "%i", &pri) != 1)
683 return RESULT_SHOWUSAGE;
684 chan->priority = pri - 1;
685 fdprintf(agi->fd, "200 result=0\n");
686 return RESULT_SUCCESS;
689 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
691 struct ast_filestream *fs;
693 struct timeval tv, start;
694 long sample_offset = 0;
698 struct ast_dsp *sildet=NULL; /* silence detector dsp */
699 int totalsilence = 0;
701 int silence = 0; /* amount of silence to allow */
702 int gotsilence = 0; /* did we timeout for silence? */
703 char *silencestr=NULL;
707 /* XXX EAGI FIXME XXX */
710 return RESULT_SHOWUSAGE;
711 if (sscanf(argv[5], "%i", &ms) != 1)
712 return RESULT_SHOWUSAGE;
715 silencestr = strchr(argv[6],'s');
716 if ((argc > 7) && (!silencestr))
717 silencestr = strchr(argv[7],'s');
718 if ((argc > 8) && (!silencestr))
719 silencestr = strchr(argv[8],'s');
722 if (strlen(silencestr) > 2) {
723 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
727 silence = atoi(silencestr);
735 rfmt = chan->readformat;
736 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
738 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
741 sildet = ast_dsp_new();
743 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
746 ast_dsp_set_threshold(sildet, 256);
749 /* backward compatibility, if no offset given, arg[6] would have been
750 * caught below and taken to be a beep, else if it is a digit then it is a
752 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
753 res = ast_streamfile(chan, "beep", chan->language);
755 if ((argc > 7) && (!strchr(argv[7], '=')))
756 res = ast_streamfile(chan, "beep", chan->language);
759 res = ast_waitstream(chan, argv[4]);
761 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
764 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
766 ast_dsp_free(sildet);
767 return RESULT_FAILURE;
771 ast_applystream(chan,fs);
772 /* really should have checks */
773 ast_seekstream(fs, sample_offset, SEEK_SET);
776 gettimeofday(&start, NULL);
777 gettimeofday(&tv, NULL);
778 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
779 res = ast_waitfor(chan, -1);
782 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
784 ast_dsp_free(sildet);
785 return RESULT_FAILURE;
789 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
792 ast_dsp_free(sildet);
793 return RESULT_FAILURE;
795 switch(f->frametype) {
797 if (strchr(argv[4], f->subclass)) {
798 /* This is an interrupting chracter */
799 sample_offset = ast_tellstream(fs);
800 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
804 ast_dsp_free(sildet);
805 return RESULT_SUCCESS;
808 case AST_FRAME_VOICE:
809 ast_writestream(fs, f);
810 /* this is a safe place to check progress since we know that fs
811 * is valid after a write, and it will then have our current
813 sample_offset = ast_tellstream(fs);
816 ast_dsp_silence(sildet, f, &dspsilence);
818 totalsilence = dspsilence;
822 if (totalsilence > silence) {
823 /* Ended happily with silence */
832 gettimeofday(&tv, NULL);
838 ast_stream_rewind(fs, silence-1000);
840 sample_offset = ast_tellstream(fs);
842 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
845 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
848 res = ast_set_read_format(chan, rfmt);
850 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
851 ast_dsp_free(sildet);
853 return RESULT_SUCCESS;
856 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
861 return RESULT_SHOWUSAGE;
862 if (sscanf(argv[2], "%d", &timeout) != 1)
863 return RESULT_SHOWUSAGE;
867 chan->whentohangup = time(NULL) + timeout;
869 chan->whentohangup = 0;
870 fdprintf(agi->fd, "200 result=0\n");
871 return RESULT_SUCCESS;
874 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
876 struct ast_channel *c;
878 /* no argument: hangup the current channel */
879 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
880 fdprintf(agi->fd, "200 result=1\n");
881 return RESULT_SUCCESS;
882 } else if (argc==2) {
883 /* one argument: look for info on the specified channel */
884 c = ast_channel_walk_locked(NULL);
886 if (strcasecmp(argv[1],c->name)==0) {
887 /* we have a matching channel */
888 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
889 fdprintf(agi->fd, "200 result=1\n");
890 ast_mutex_unlock(&c->lock);
891 return RESULT_SUCCESS;
893 ast_mutex_unlock(&c->lock);
894 c = ast_channel_walk_locked(c);
896 /* if we get this far no channel name matched the argument given */
897 fdprintf(agi->fd, "200 result=-1\n");
898 return RESULT_SUCCESS;
900 return RESULT_SHOWUSAGE;
904 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
910 return RESULT_SHOWUSAGE;
912 if (option_verbose > 2)
913 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
915 app = pbx_findapp(argv[1]);
918 res = pbx_exec(chan, app, argv[2], 1);
920 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
923 fdprintf(agi->fd, "200 result=%d\n", res);
928 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
931 char *l = NULL, *n = NULL;
933 strncpy(tmp, argv[2], sizeof(tmp) - 1);
934 ast_callerid_parse(tmp, &n, &l);
936 ast_shrink_phone_number(l);
941 ast_set_callerid(chan, l, n, NULL);
944 fdprintf(agi->fd, "200 result=1\n");
945 return RESULT_SUCCESS;
948 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
950 struct ast_channel *c;
952 /* no argument: supply info on the current channel */
953 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
954 return RESULT_SUCCESS;
955 } else if (argc==3) {
956 /* one argument: look for info on the specified channel */
957 c = ast_channel_walk_locked(NULL);
959 if (strcasecmp(argv[2],c->name)==0) {
960 fdprintf(agi->fd, "200 result=%d\n", c->_state);
961 ast_mutex_unlock(&c->lock);
962 return RESULT_SUCCESS;
964 ast_mutex_unlock(&c->lock);
965 c = ast_channel_walk_locked(c);
967 /* if we get this far no channel name matched the argument given */
968 fdprintf(agi->fd, "200 result=-1\n");
969 return RESULT_SUCCESS;
971 return RESULT_SHOWUSAGE;
975 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
978 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
980 fdprintf(agi->fd, "200 result=1\n");
981 return RESULT_SUCCESS;
984 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
989 return RESULT_SHOWUSAGE;
990 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
992 fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
994 fdprintf(agi->fd, "200 result=0\n");
996 return RESULT_SUCCESS;
999 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1002 struct ast_channel *chan2=NULL;
1003 if ((argc != 4) && (argc != 5))
1004 return RESULT_SHOWUSAGE;
1006 while((chan2 = ast_channel_walk_locked(chan2))) {
1007 if (!strcmp(chan2->name, argv[4]))
1009 ast_mutex_unlock(&chan2->lock);
1015 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
1016 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1018 fdprintf(agi->fd, "200 result=0\n");
1020 if (chan2 && (chan2 != chan))
1021 ast_mutex_unlock(&chan2->lock);
1022 return RESULT_SUCCESS;
1025 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1031 return RESULT_SHOWUSAGE;
1034 sscanf(argv[2], "%d", &level);
1038 prefix = VERBOSE_PREFIX_4;
1041 prefix = VERBOSE_PREFIX_3;
1044 prefix = VERBOSE_PREFIX_2;
1048 prefix = VERBOSE_PREFIX_1;
1052 if (level <= option_verbose)
1053 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
1055 fdprintf(agi->fd, "200 result=1\n");
1057 return RESULT_SUCCESS;
1060 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1065 return RESULT_SHOWUSAGE;
1066 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
1068 fdprintf(agi->fd, "200 result=0\n");
1070 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1072 return RESULT_SUCCESS;
1075 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1079 return RESULT_SHOWUSAGE;
1080 res = ast_db_put(argv[2], argv[3], argv[4]);
1082 fdprintf(agi->fd, "200 result=0\n");
1084 fdprintf(agi->fd, "200 result=1\n");
1086 return RESULT_SUCCESS;
1089 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1093 return RESULT_SHOWUSAGE;
1094 res = ast_db_del(argv[2], argv[3]);
1096 fdprintf(agi->fd, "200 result=0\n");
1098 fdprintf(agi->fd, "200 result=1\n");
1100 return RESULT_SUCCESS;
1103 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1106 if ((argc < 3) || (argc > 4))
1107 return RESULT_SHOWUSAGE;
1109 res = ast_db_deltree(argv[2], argv[3]);
1111 res = ast_db_deltree(argv[2], NULL);
1114 fdprintf(agi->fd, "200 result=0\n");
1116 fdprintf(agi->fd, "200 result=1\n");
1117 return RESULT_SUCCESS;
1120 static char debug_usage[] =
1121 "Usage: agi debug\n"
1122 " Enables dumping of AGI transactions for debugging purposes\n";
1124 static char no_debug_usage[] =
1125 "Usage: agi no debug\n"
1126 " Disables dumping of AGI transactions for debugging purposes\n";
1128 static int agi_do_debug(int fd, int argc, char *argv[])
1131 return RESULT_SHOWUSAGE;
1133 ast_cli(fd, "AGI Debugging Enabled\n");
1134 return RESULT_SUCCESS;
1137 static int agi_no_debug(int fd, int argc, char *argv[])
1140 return RESULT_SHOWUSAGE;
1142 ast_cli(fd, "AGI Debugging Disabled\n");
1143 return RESULT_SUCCESS;
1146 static struct ast_cli_entry cli_debug =
1147 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1149 static struct ast_cli_entry cli_no_debug =
1150 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1152 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1154 fdprintf(agi->fd, "200 result=0\n");
1155 return RESULT_SUCCESS;
1158 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1160 if (!strncasecmp(argv[2],"on",2)) {
1162 ast_moh_start(chan, argv[3]);
1164 ast_moh_start(chan, NULL);
1166 if (!strncasecmp(argv[2],"off",3)) {
1169 fdprintf(agi->fd, "200 result=0\n");
1170 return RESULT_SUCCESS;
1173 static char usage_setmusic[] =
1174 " Usage: SET MUSIC ON <on|off> <class>\n"
1175 " Enables/Disables the music on hold generator. If <class> is\n"
1176 " not specified then the default music on hold class will be used.\n"
1177 " Always returns 0\n";
1179 static char usage_dbput[] =
1180 " Usage: DATABASE PUT <family> <key> <value>\n"
1181 " Adds or updates an entry in the Asterisk database for a\n"
1182 " given family, key, and value.\n"
1183 " Returns 1 if succesful, 0 otherwise\n";
1185 static char usage_dbget[] =
1186 " Usage: DATABASE GET <family> <key>\n"
1187 " Retrieves an entry in the Asterisk database for a\n"
1188 " given family and key.\n"
1189 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1190 " is set and returns the variable in parenthesis\n"
1191 " example return code: 200 result=1 (testvariable)\n";
1193 static char usage_dbdel[] =
1194 " Usage: DATABASE DEL <family> <key>\n"
1195 " Deletes an entry in the Asterisk database for a\n"
1196 " given family and key.\n"
1197 " Returns 1 if succesful, 0 otherwise\n";
1199 static char usage_dbdeltree[] =
1200 " Usage: DATABASE DELTREE <family> [keytree]\n"
1201 " Deletes a family or specific keytree withing a family\n"
1202 " in the Asterisk database.\n"
1203 " Returns 1 if succesful, 0 otherwise\n";
1205 static char usage_verbose[] =
1206 " Usage: VERBOSE <message> <level>\n"
1207 " Sends <message> to the console via verbose message system.\n"
1208 " <level> is the the verbose level (1-4)\n"
1209 " Always returns 1\n";
1211 static char usage_getvariable[] =
1212 " Usage: GET VARIABLE <variablename>\n"
1213 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1214 " is set and returns the variable in parenthesis\n"
1215 " example return code: 200 result=1 (testvariable)\n";
1217 static char usage_getvariablefull[] =
1218 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
1219 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
1220 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
1221 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
1222 " example return code: 200 result=1 (testvariable)\n";
1224 static char usage_setvariable[] =
1225 " Usage: SET VARIABLE <variablename> <value>\n";
1227 static char usage_channelstatus[] =
1228 " Usage: CHANNEL STATUS [<channelname>]\n"
1229 " Returns the status of the specified channel.\n"
1230 " If no channel name is given the returns the status of the\n"
1231 " current channel.\n"
1233 " 0 Channel is down and available\n"
1234 " 1 Channel is down, but reserved\n"
1235 " 2 Channel is off hook\n"
1236 " 3 Digits (or equivalent) have been dialed\n"
1237 " 4 Line is ringing\n"
1238 " 5 Remote end is ringing\n"
1240 " 7 Line is busy\n";
1242 static char usage_setcallerid[] =
1243 " Usage: SET CALLERID <number>\n"
1244 " Changes the callerid of the current channel.\n";
1246 static char usage_exec[] =
1247 " Usage: EXEC <application> <options>\n"
1248 " Executes <application> with given <options>.\n"
1249 " Returns whatever the application returns, or -2 on failure to find application\n";
1251 static char usage_hangup[] =
1252 " Usage: HANGUP [<channelname>]\n"
1253 " Hangs up the specified channel.\n"
1254 " If no channel name is given, hangs up the current channel\n";
1256 static char usage_answer[] =
1258 " Answers channel if not already in answer state. Returns -1 on\n"
1259 " channel failure, or 0 if successful.\n";
1261 static char usage_waitfordigit[] =
1262 " Usage: WAIT FOR DIGIT <timeout>\n"
1263 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1264 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1265 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1266 " for the timeout value if you desire the call to block indefinitely.\n";
1268 static char usage_sendtext[] =
1269 " Usage: SEND TEXT \"<text to send>\"\n"
1270 " Sends the given text on a channel. Most channels do not support the\n"
1271 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1272 " support text transmission. Returns -1 only on error/hangup. Text\n"
1273 " consisting of greater than one word should be placed in quotes since the\n"
1274 " command only accepts a single argument.\n";
1276 static char usage_recvchar[] =
1277 " Usage: RECEIVE CHAR <timeout>\n"
1278 " Receives a character of text on a channel. Specify timeout to be the\n"
1279 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1280 " do not support the reception of text. Returns the decimal value of the character\n"
1281 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1282 " -1 only on error/hangup.\n";
1284 static char usage_tddmode[] =
1285 " Usage: TDD MODE <on|off>\n"
1286 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1287 " successful, or 0 if channel is not TDD-capable.\n";
1289 static char usage_sendimage[] =
1290 " Usage: SEND IMAGE <image>\n"
1291 " Sends the given image on a channel. Most channels do not support the\n"
1292 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1293 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1294 " should not include extensions.\n";
1296 static char usage_streamfile[] =
1297 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1298 " Send the given file, allowing playback to be interrupted by the given\n"
1299 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1300 " permitted. If sample offset is provided then the audio will seek to sample\n"
1301 " offset before play starts. Returns 0 if playback completes without a digit\n"
1302 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1303 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1304 " extension must not be included in the filename.\n";
1306 static char usage_getoption[] =
1307 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
1308 " Exactly like the STREAM FILE but used with a timeout option\n";
1310 static char usage_saynumber[] =
1311 " Usage: SAY NUMBER <number> <escape digits>\n"
1312 " Say a given number, returning early if any of the given DTMF digits\n"
1313 " are received on the channel. Returns 0 if playback completes without a digit\n"
1314 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1315 " -1 on error/hangup.\n";
1317 static char usage_saydigits[] =
1318 " Usage: SAY DIGITS <number> <escape digits>\n"
1319 " Say a given digit string, returning early if any of the given DTMF digits\n"
1320 " are received on the channel. Returns 0 if playback completes without a digit\n"
1321 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1322 " -1 on error/hangup.\n";
1324 static char usage_sayalpha[] =
1325 " Usage: SAY ALPHA <number> <escape digits>\n"
1326 " Say a given character string, returning early if any of the given DTMF digits\n"
1327 " are received on the channel. Returns 0 if playback completes without a digit\n"
1328 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1329 " -1 on error/hangup.\n";
1331 static char usage_saytime[] =
1332 " Usage: SAY TIME <time> <escape digits>\n"
1333 " Say a given time, returning early if any of the given DTMF digits are\n"
1334 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1335 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1336 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1337 " digit if one was pressed or -1 on error/hangup.\n";
1339 static char usage_sayphonetic[] =
1340 " Usage: SAY PHONETIC <string> <escape digits>\n"
1341 " Say a given character string with phonetics, returning early if any of the given DTMF digits\n"
1342 " are received on the channel. Returns 0 if playback completes without a digit\n"
1343 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1344 " -1 on error/hangup.\n";
1346 static char usage_getdata[] =
1347 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1348 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
1349 "from the channel at the other end.\n";
1351 static char usage_setcontext[] =
1352 " Usage: SET CONTEXT <desired context>\n"
1353 " Sets the context for continuation upon exiting the application.\n";
1355 static char usage_setextension[] =
1356 " Usage: SET EXTENSION <new extension>\n"
1357 " Changes the extension for continuation upon exiting the application.\n";
1359 static char usage_setpriority[] =
1360 " Usage: SET PRIORITY <num>\n"
1361 " Changes the priority for continuation upon exiting the application.\n";
1363 static char usage_recordfile[] =
1364 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1365 " Record to a file until a given dtmf digit in the sequence is received\n"
1366 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1367 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1368 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1369 " the offset without exceeding the end of the file. \"silence\" is the number\n"
1370 " of seconds of silence allowed before the function returns despite the\n"
1371 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1372 " preceeded by \"s=\" and is optional.\n";
1375 static char usage_autohangup[] =
1376 " Usage: SET AUTOHANGUP <time>\n"
1377 " Cause the channel to automatically hangup at <time> seconds in the\n"
1378 "future. Of course it can be hungup before then as well. Setting to\n"
1379 "0 will cause the autohangup feature to be disabled on this channel.\n";
1381 static char usage_noop[] =
1385 static agi_command commands[MAX_COMMANDS] = {
1386 { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1387 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1388 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1389 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1390 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1391 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1392 { { "get", "option", NULL }, handle_getoption, "Stream File", usage_getoption },
1393 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1394 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1395 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
1396 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1397 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1398 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1399 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1400 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1401 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1402 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1403 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1404 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1405 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1406 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1407 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1408 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1409 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1410 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1411 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
1412 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1413 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1414 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1415 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1416 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1417 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1418 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1421 static void join(char *s, size_t len, char *w[])
1424 /* Join words into a string */
1429 for (x=0;w[x];x++) {
1431 strncat(s, " ", len - strlen(s) - 1);
1432 strncat(s, w[x], len - strlen(s) - 1);
1436 static int help_workhorse(int fd, char *match[])
1441 struct agi_command *e;
1443 join(matchstr, sizeof(matchstr), match);
1444 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1445 if (!commands[x].cmda[0]) break;
1448 join(fullcmd, sizeof(fullcmd), e->cmda);
1449 /* Hide commands that start with '_' */
1450 if (fullcmd[0] == '_')
1453 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1457 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1462 int agi_register(agi_command *agi)
1465 for (x=0;x<MAX_COMMANDS - 1;x++) {
1466 if (commands[x].cmda[0] == agi->cmda[0]) {
1467 ast_log(LOG_WARNING, "Command already registered!\n");
1471 for (x=0;x<MAX_COMMANDS - 1;x++) {
1472 if (!commands[x].cmda[0]) {
1477 ast_log(LOG_WARNING, "No more room for new commands!\n");
1481 void agi_unregister(agi_command *agi)
1484 for (x=0;x<MAX_COMMANDS - 1;x++) {
1485 if (commands[x].cmda[0] == agi->cmda[0]) {
1486 memset(&commands[x], 0, sizeof(agi_command));
1491 static agi_command *find_command(char *cmds[], int exact)
1496 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1497 if (!commands[x].cmda[0]) break;
1498 /* start optimistic */
1500 for (y=0;match && cmds[y]; y++) {
1501 /* If there are no more words in the command (and we're looking for
1502 an exact match) or there is a difference between the two words,
1503 then this is not a match */
1504 if (!commands[x].cmda[y] && !exact)
1506 /* don't segfault if the next part of a command doesn't exist */
1507 if (!commands[x].cmda[y]) return NULL;
1508 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1511 /* If more words are needed to complete the command then this is not
1512 a candidate (unless we're looking for a really inexact answer */
1513 if ((exact > -1) && commands[x].cmda[y])
1516 return &commands[x];
1522 static int parse_args(char *s, int *max, char *argv[])
1534 /* If it's escaped, put a literal quote */
1539 if (quoted && whitespace) {
1540 /* If we're starting a quote, coming off white space start a new word, too */
1548 if (!quoted && !escaped) {
1549 /* If we're not quoted, mark this as whitespace, and
1550 end the previous argument */
1554 /* Otherwise, just treat it as anything else */
1558 /* If we're escaped, print a literal, otherwise enable escaping */
1568 if (x >= MAX_ARGS -1) {
1569 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1572 /* Coming off of whitespace, start the next argument */
1581 /* Null terminate */
1588 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1590 char *argv[MAX_ARGS];
1595 parse_args(buf, &argc, argv);
1598 for (x=0;x<argc;x++)
1599 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1601 c = find_command(argv, 0);
1603 res = c->handler(chan, agi, argc, argv);
1605 case RESULT_SHOWUSAGE:
1606 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1607 fdprintf(agi->fd, c->usage);
1608 fdprintf(agi->fd, "520 End of proper usage.\n");
1610 case AST_PBX_KEEPALIVE:
1611 /* We've been asked to keep alive, so do so */
1612 return AST_PBX_KEEPALIVE;
1614 case RESULT_FAILURE:
1615 /* They've already given the failure. We've been hung up on so handle this
1620 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1625 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1627 struct ast_channel *c;
1630 int returnstatus = 0;
1631 struct ast_frame *f;
1634 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1635 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1638 if (!(readf = fdopen(agi->ctrl, "r"))) {
1639 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1646 setup_env(chan, request, agi->fd, (agi->audio > -1));
1649 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1652 /* Idle the channel until we get a command */
1655 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1659 /* If it's voice, write it to the audio pipe */
1660 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1661 /* Write, ignoring errors */
1662 write(agi->audio, f->data, f->datalen);
1666 } else if (outfd > -1) {
1668 if (!fgets(buf, sizeof(buf), readf)) {
1669 /* Program terminated */
1672 if (option_verbose > 2)
1673 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1674 /* No need to kill the pid anymore, since they closed us */
1678 /* get rid of trailing newline, if any */
1679 if (*buf && buf[strlen(buf) - 1] == '\n')
1680 buf[strlen(buf) - 1] = 0;
1682 ast_verbose("AGI Rx << %s\n", buf);
1683 returnstatus |= agi_handle_command(chan, agi, buf);
1684 /* If the handle_command returns -1, we need to stop */
1685 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1690 ast_log(LOG_WARNING, "No channel, no fd?\n");
1696 /* Notify process */
1700 return returnstatus;
1703 static int handle_showagi(int fd, int argc, char *argv[]) {
1704 struct agi_command *e;
1707 return RESULT_SHOWUSAGE;
1709 e = find_command(argv + 2, 1);
1711 ast_cli(fd, e->usage);
1713 if (find_command(argv + 2, -1)) {
1714 return help_workhorse(fd, argv + 1);
1716 join(fullcmd, sizeof(fullcmd), argv+1);
1717 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1721 return help_workhorse(fd, NULL);
1723 return RESULT_SUCCESS;
1726 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1727 struct agi_command *e;
1734 return RESULT_SHOWUSAGE;
1736 if (!(htmlfile = fopen(argv[2], "wt"))) {
1737 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1738 return RESULT_SHOWUSAGE;
1741 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1742 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1745 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1747 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1749 if (!commands[x].cmda[0]) break;
1752 join(fullcmd, sizeof(fullcmd), e->cmda);
1753 /* Hide commands that start with '_' */
1754 if (fullcmd[0] == '_')
1757 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1758 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1762 tempstr = strsep(&stringp, "\n");
1764 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1766 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1767 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1768 fprintf(htmlfile, "%s<BR>\n",tempstr);
1771 fprintf(htmlfile, "</TD></TR>\n");
1772 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1776 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1778 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1779 return RESULT_SUCCESS;
1782 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1785 struct localuser *u;
1786 char *argv[MAX_ARGS];
1788 char *tmp = (char *)buf;
1795 if (!data || ast_strlen_zero(data)) {
1796 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1799 strncpy(buf, data, sizeof(buf) - 1);
1801 memset(&agi, 0, sizeof(agi));
1802 while ((stringp = strsep(&tmp, "|"))) {
1803 argv[argc++] = stringp;
1809 /* Answer if need be */
1810 if (chan->_state != AST_STATE_UP) {
1811 if (ast_answer(chan)) {
1812 LOCAL_USER_REMOVE(u);
1817 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1822 res = run_agi(chan, argv[0], &agi, pid, dead);
1827 LOCAL_USER_REMOVE(u);
1831 static int agi_exec(struct ast_channel *chan, void *data)
1833 if (chan->_softhangup)
1834 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1835 return agi_exec_full(chan, data, 0, 0);
1838 static int eagi_exec(struct ast_channel *chan, void *data)
1842 if (chan->_softhangup)
1843 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1844 readformat = chan->readformat;
1845 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1846 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1849 res = agi_exec_full(chan, data, 1, 0);
1851 if (ast_set_read_format(chan, readformat)) {
1852 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1858 static int deadagi_exec(struct ast_channel *chan, void *data)
1860 return agi_exec_full(chan, data, 0, 1);
1863 static char showagi_help[] =
1864 "Usage: show agi [topic]\n"
1865 " When called with a topic as an argument, displays usage\n"
1866 " information on the given command. If called without a\n"
1867 " topic, it provides a list of AGI commands.\n";
1870 static char dumpagihtml_help[] =
1871 "Usage: dump agihtml <filename>\n"
1872 " Dumps the agi command list in html format to given filename\n";
1874 static struct ast_cli_entry showagi =
1875 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1877 static struct ast_cli_entry dumpagihtml =
1878 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1880 int unload_module(void)
1882 STANDARD_HANGUP_LOCALUSERS;
1883 ast_cli_unregister(&showagi);
1884 ast_cli_unregister(&dumpagihtml);
1885 ast_cli_unregister(&cli_debug);
1886 ast_cli_unregister(&cli_no_debug);
1887 ast_unregister_application(eapp);
1888 ast_unregister_application(deadapp);
1889 return ast_unregister_application(app);
1892 int load_module(void)
1894 ast_cli_register(&showagi);
1895 ast_cli_register(&dumpagihtml);
1896 ast_cli_register(&cli_debug);
1897 ast_cli_register(&cli_no_debug);
1898 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
1899 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1900 return ast_register_application(app, agi_exec, synopsis, descrip);
1903 char *description(void)
1911 STANDARD_USECOUNT(res);
1917 return ASTERISK_GPL_KEY;