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 return RESULT_SUCCESS;
438 ast_seekstream(fs, 0, SEEK_END);
439 max_length = ast_tellstream(fs);
440 ast_seekstream(fs, sample_offset, SEEK_SET);
441 res = ast_applystream(chan, fs);
442 res = ast_playstream(fs);
444 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
446 return RESULT_SHOWUSAGE;
448 return RESULT_FAILURE;
450 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
451 /* this is to check for if ast_waitstream closed the stream, we probably are at
452 * the end of the stream, return that amount, else check for the amount */
453 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
454 ast_stopstream(chan);
456 /* Stop this command, don't print a result line, as there is a new command */
457 return RESULT_SUCCESS;
459 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
461 return RESULT_SUCCESS;
463 return RESULT_FAILURE;
466 /* get option - really similar to the handle_streamfile, but with a timeout */
467 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
470 struct ast_filestream *fs;
471 long sample_offset = 0;
474 char *edigits = NULL;
476 if ( argc < 4 || argc > 5 )
477 return RESULT_SHOWUSAGE;
483 timeout = atoi(argv[4]);
484 else if (chan->pbx->dtimeout) {
485 /* by default dtimeout is set to 5sec */
486 timeout = chan->pbx->dtimeout * 1000; /* in msec */
489 fs = ast_openstream(chan, argv[2], chan->language);
491 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
492 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
493 return RESULT_FAILURE;
495 if (option_verbose > 2)
496 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
498 ast_seekstream(fs, 0, SEEK_END);
499 max_length = ast_tellstream(fs);
500 ast_seekstream(fs, sample_offset, SEEK_SET);
501 res = ast_applystream(chan, fs);
502 res = ast_playstream(fs);
504 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
506 return RESULT_SHOWUSAGE;
508 return RESULT_FAILURE;
510 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
511 /* this is to check for if ast_waitstream closed the stream, we probably are at
512 * the end of the stream, return that amount, else check for the amount */
513 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
514 ast_stopstream(chan);
516 /* Stop this command, don't print a result line, as there is a new command */
517 return RESULT_SUCCESS;
520 /* If the user didnt press a key, wait for digitTimeout*/
522 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
523 /* Make sure the new result is in the escape digits of the GET OPTION */
524 if ( !strchr(edigits,res) )
528 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
530 return RESULT_SUCCESS;
532 return RESULT_FAILURE;
538 /*--- handle_saynumber: Say number in various language syntaxes ---*/
539 /* Need to add option for gender here as well. Coders wanted */
540 /* While waiting, we're sending a (char *) NULL. */
541 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
546 return RESULT_SHOWUSAGE;
547 if (sscanf(argv[2], "%i", &num) != 1)
548 return RESULT_SHOWUSAGE;
549 res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl);
551 return RESULT_SUCCESS;
552 fdprintf(agi->fd, "200 result=%d\n", res);
554 return RESULT_SUCCESS;
556 return RESULT_FAILURE;
559 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
565 return RESULT_SHOWUSAGE;
566 if (sscanf(argv[2], "%i", &num) != 1)
567 return RESULT_SHOWUSAGE;
569 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
570 if (res == 1) /* New command */
571 return RESULT_SUCCESS;
572 fdprintf(agi->fd, "200 result=%d\n", res);
574 return RESULT_SUCCESS;
576 return RESULT_FAILURE;
579 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
584 return RESULT_SHOWUSAGE;
586 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
587 if (res == 1) /* New command */
588 return RESULT_SUCCESS;
589 fdprintf(agi->fd, "200 result=%d\n", res);
591 return RESULT_SUCCESS;
593 return RESULT_FAILURE;
596 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
601 return RESULT_SHOWUSAGE;
602 if (sscanf(argv[2], "%i", &num) != 1)
603 return RESULT_SHOWUSAGE;
604 res = ast_say_time(chan, num, argv[3], chan->language);
606 return RESULT_SUCCESS;
607 fdprintf(agi->fd, "200 result=%d\n", res);
609 return RESULT_SUCCESS;
611 return RESULT_FAILURE;
614 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
619 return RESULT_SHOWUSAGE;
621 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
622 if (res == 1) /* New command */
623 return RESULT_SUCCESS;
624 fdprintf(agi->fd, "200 result=%d\n", res);
626 return RESULT_SUCCESS;
628 return RESULT_FAILURE;
631 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
639 return RESULT_SHOWUSAGE;
640 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
641 if (argc >= 5) max = atoi(argv[4]); else max = 1024;
642 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
643 if (res == 2) /* New command */
644 return RESULT_SUCCESS;
646 fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
648 fdprintf(agi->fd, "200 result=-1\n");
650 fdprintf(agi->fd, "200 result=%s\n", data);
652 return RESULT_SUCCESS;
654 return RESULT_FAILURE;
657 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
661 return RESULT_SHOWUSAGE;
662 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
663 fdprintf(agi->fd, "200 result=0\n");
664 return RESULT_SUCCESS;
667 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
670 return RESULT_SHOWUSAGE;
671 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
672 fdprintf(agi->fd, "200 result=0\n");
673 return RESULT_SUCCESS;
676 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
680 return RESULT_SHOWUSAGE;
681 if (sscanf(argv[2], "%i", &pri) != 1)
682 return RESULT_SHOWUSAGE;
683 chan->priority = pri - 1;
684 fdprintf(agi->fd, "200 result=0\n");
685 return RESULT_SUCCESS;
688 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
690 struct ast_filestream *fs;
692 struct timeval tv, start;
693 long sample_offset = 0;
697 struct ast_dsp *sildet=NULL; /* silence detector dsp */
698 int totalsilence = 0;
700 int silence = 0; /* amount of silence to allow */
701 int gotsilence = 0; /* did we timeout for silence? */
702 char *silencestr=NULL;
706 /* XXX EAGI FIXME XXX */
709 return RESULT_SHOWUSAGE;
710 if (sscanf(argv[5], "%i", &ms) != 1)
711 return RESULT_SHOWUSAGE;
714 silencestr = strchr(argv[6],'s');
715 if ((argc > 7) && (!silencestr))
716 silencestr = strchr(argv[7],'s');
717 if ((argc > 8) && (!silencestr))
718 silencestr = strchr(argv[8],'s');
721 if (strlen(silencestr) > 2) {
722 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
726 silence = atoi(silencestr);
734 rfmt = chan->readformat;
735 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
737 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
740 sildet = ast_dsp_new();
742 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
745 ast_dsp_set_threshold(sildet, 256);
748 /* backward compatibility, if no offset given, arg[6] would have been
749 * caught below and taken to be a beep, else if it is a digit then it is a
751 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
752 res = ast_streamfile(chan, "beep", chan->language);
754 if ((argc > 7) && (!strchr(argv[7], '=')))
755 res = ast_streamfile(chan, "beep", chan->language);
758 res = ast_waitstream(chan, argv[4]);
760 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
763 fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
765 ast_dsp_free(sildet);
766 return RESULT_FAILURE;
770 ast_applystream(chan,fs);
771 /* really should have checks */
772 ast_seekstream(fs, sample_offset, SEEK_SET);
775 gettimeofday(&start, NULL);
776 gettimeofday(&tv, NULL);
777 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
778 res = ast_waitfor(chan, -1);
781 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
783 ast_dsp_free(sildet);
784 return RESULT_FAILURE;
788 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
791 ast_dsp_free(sildet);
792 return RESULT_FAILURE;
794 switch(f->frametype) {
796 if (strchr(argv[4], f->subclass)) {
797 /* This is an interrupting chracter */
798 sample_offset = ast_tellstream(fs);
799 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
803 ast_dsp_free(sildet);
804 return RESULT_SUCCESS;
807 case AST_FRAME_VOICE:
808 ast_writestream(fs, f);
809 /* this is a safe place to check progress since we know that fs
810 * is valid after a write, and it will then have our current
812 sample_offset = ast_tellstream(fs);
815 ast_dsp_silence(sildet, f, &dspsilence);
817 totalsilence = dspsilence;
821 if (totalsilence > silence) {
822 /* Ended happily with silence */
831 gettimeofday(&tv, NULL);
837 ast_stream_rewind(fs, silence-1000);
839 sample_offset = ast_tellstream(fs);
841 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
844 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
847 res = ast_set_read_format(chan, rfmt);
849 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
850 ast_dsp_free(sildet);
852 return RESULT_SUCCESS;
855 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
860 return RESULT_SHOWUSAGE;
861 if (sscanf(argv[2], "%d", &timeout) != 1)
862 return RESULT_SHOWUSAGE;
866 chan->whentohangup = time(NULL) + timeout;
868 chan->whentohangup = 0;
869 fdprintf(agi->fd, "200 result=0\n");
870 return RESULT_SUCCESS;
873 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
875 struct ast_channel *c;
877 /* no argument: hangup the current channel */
878 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
879 fdprintf(agi->fd, "200 result=1\n");
880 return RESULT_SUCCESS;
881 } else if (argc==2) {
882 /* one argument: look for info on the specified channel */
883 c = ast_channel_walk_locked(NULL);
885 if (strcasecmp(argv[1],c->name)==0) {
886 /* we have a matching channel */
887 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
888 fdprintf(agi->fd, "200 result=1\n");
889 ast_mutex_unlock(&c->lock);
890 return RESULT_SUCCESS;
892 ast_mutex_unlock(&c->lock);
893 c = ast_channel_walk_locked(c);
895 /* if we get this far no channel name matched the argument given */
896 fdprintf(agi->fd, "200 result=-1\n");
897 return RESULT_SUCCESS;
899 return RESULT_SHOWUSAGE;
903 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
909 return RESULT_SHOWUSAGE;
911 if (option_verbose > 2)
912 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
914 app = pbx_findapp(argv[1]);
917 res = pbx_exec(chan, app, argv[2], 1);
919 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
922 fdprintf(agi->fd, "200 result=%d\n", res);
927 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
930 char *l = NULL, *n = NULL;
932 strncpy(tmp, argv[2], sizeof(tmp) - 1);
933 ast_callerid_parse(tmp, &n, &l);
935 ast_shrink_phone_number(l);
940 ast_set_callerid(chan, l, n, NULL);
943 fdprintf(agi->fd, "200 result=1\n");
944 return RESULT_SUCCESS;
947 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
949 struct ast_channel *c;
951 /* no argument: supply info on the current channel */
952 fdprintf(agi->fd, "200 result=%d\n", chan->_state);
953 return RESULT_SUCCESS;
954 } else if (argc==3) {
955 /* one argument: look for info on the specified channel */
956 c = ast_channel_walk_locked(NULL);
958 if (strcasecmp(argv[2],c->name)==0) {
959 fdprintf(agi->fd, "200 result=%d\n", c->_state);
960 ast_mutex_unlock(&c->lock);
961 return RESULT_SUCCESS;
963 ast_mutex_unlock(&c->lock);
964 c = ast_channel_walk_locked(c);
966 /* if we get this far no channel name matched the argument given */
967 fdprintf(agi->fd, "200 result=-1\n");
968 return RESULT_SUCCESS;
970 return RESULT_SHOWUSAGE;
974 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
977 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
979 fdprintf(agi->fd, "200 result=1\n");
980 return RESULT_SUCCESS;
983 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
988 return RESULT_SHOWUSAGE;
989 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
991 fdprintf(agi->fd, "200 result=1 (%s)\n", ret);
993 fdprintf(agi->fd, "200 result=0\n");
995 return RESULT_SUCCESS;
998 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1001 struct ast_channel *chan2=NULL;
1002 if ((argc != 4) && (argc != 5))
1003 return RESULT_SHOWUSAGE;
1005 while((chan2 = ast_channel_walk_locked(chan2))) {
1006 if (!strcmp(chan2->name, argv[4]))
1008 ast_mutex_unlock(&chan2->lock);
1014 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
1015 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1017 fdprintf(agi->fd, "200 result=0\n");
1019 if (chan2 && (chan2 != chan))
1020 ast_mutex_unlock(&chan2->lock);
1021 return RESULT_SUCCESS;
1024 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1030 return RESULT_SHOWUSAGE;
1033 sscanf(argv[2], "%d", &level);
1037 prefix = VERBOSE_PREFIX_4;
1040 prefix = VERBOSE_PREFIX_3;
1043 prefix = VERBOSE_PREFIX_2;
1047 prefix = VERBOSE_PREFIX_1;
1051 if (level <= option_verbose)
1052 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
1054 fdprintf(agi->fd, "200 result=1\n");
1056 return RESULT_SUCCESS;
1059 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1064 return RESULT_SHOWUSAGE;
1065 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
1067 fdprintf(agi->fd, "200 result=0\n");
1069 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
1071 return RESULT_SUCCESS;
1074 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1078 return RESULT_SHOWUSAGE;
1079 res = ast_db_put(argv[2], argv[3], argv[4]);
1081 fdprintf(agi->fd, "200 result=0\n");
1083 fdprintf(agi->fd, "200 result=1\n");
1085 return RESULT_SUCCESS;
1088 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1092 return RESULT_SHOWUSAGE;
1093 res = ast_db_del(argv[2], argv[3]);
1095 fdprintf(agi->fd, "200 result=0\n");
1097 fdprintf(agi->fd, "200 result=1\n");
1099 return RESULT_SUCCESS;
1102 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
1105 if ((argc < 3) || (argc > 4))
1106 return RESULT_SHOWUSAGE;
1108 res = ast_db_deltree(argv[2], argv[3]);
1110 res = ast_db_deltree(argv[2], NULL);
1113 fdprintf(agi->fd, "200 result=0\n");
1115 fdprintf(agi->fd, "200 result=1\n");
1116 return RESULT_SUCCESS;
1119 static char debug_usage[] =
1120 "Usage: agi debug\n"
1121 " Enables dumping of AGI transactions for debugging purposes\n";
1123 static char no_debug_usage[] =
1124 "Usage: agi no debug\n"
1125 " Disables dumping of AGI transactions for debugging purposes\n";
1127 static int agi_do_debug(int fd, int argc, char *argv[])
1130 return RESULT_SHOWUSAGE;
1132 ast_cli(fd, "AGI Debugging Enabled\n");
1133 return RESULT_SUCCESS;
1136 static int agi_no_debug(int fd, int argc, char *argv[])
1139 return RESULT_SHOWUSAGE;
1141 ast_cli(fd, "AGI Debugging Disabled\n");
1142 return RESULT_SUCCESS;
1145 static struct ast_cli_entry cli_debug =
1146 { { "agi", "debug", NULL }, agi_do_debug, "Enable AGI debugging", debug_usage };
1148 static struct ast_cli_entry cli_no_debug =
1149 { { "agi", "no", "debug", NULL }, agi_no_debug, "Disable AGI debugging", no_debug_usage };
1151 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
1153 fdprintf(agi->fd, "200 result=0\n");
1154 return RESULT_SUCCESS;
1157 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
1159 if (!strncasecmp(argv[2],"on",2)) {
1161 ast_moh_start(chan, argv[3]);
1163 ast_moh_start(chan, NULL);
1165 if (!strncasecmp(argv[2],"off",3)) {
1168 fdprintf(agi->fd, "200 result=0\n");
1169 return RESULT_SUCCESS;
1172 static char usage_setmusic[] =
1173 " Usage: SET MUSIC ON <on|off> <class>\n"
1174 " Enables/Disables the music on hold generator. If <class> is\n"
1175 " not specified, then the default music on hold class will be used.\n"
1176 " Always returns 0.\n";
1178 static char usage_dbput[] =
1179 " Usage: DATABASE PUT <family> <key> <value>\n"
1180 " Adds or updates an entry in the Asterisk database for a\n"
1181 " given family, key, and value.\n"
1182 " Returns 1 if successful, 0 otherwise.\n";
1184 static char usage_dbget[] =
1185 " Usage: DATABASE GET <family> <key>\n"
1186 " Retrieves an entry in the Asterisk database for a\n"
1187 " given family and key.\n"
1188 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
1189 " is set and returns the variable in parentheses.\n"
1190 " Example return code: 200 result=1 (testvariable)\n";
1192 static char usage_dbdel[] =
1193 " Usage: DATABASE DEL <family> <key>\n"
1194 " Deletes an entry in the Asterisk database for a\n"
1195 " given family and key.\n"
1196 " Returns 1 if successful, 0 otherwise.\n";
1198 static char usage_dbdeltree[] =
1199 " Usage: DATABASE DELTREE <family> [keytree]\n"
1200 " Deletes a family or specific keytree within a family\n"
1201 " in the Asterisk database.\n"
1202 " Returns 1 if successful, 0 otherwise.\n";
1204 static char usage_verbose[] =
1205 " Usage: VERBOSE <message> <level>\n"
1206 " Sends <message> to the console via verbose message system.\n"
1207 " <level> is the the verbose level (1-4)\n"
1208 " Always returns 1.\n";
1210 static char usage_getvariable[] =
1211 " Usage: GET VARIABLE <variablename>\n"
1212 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
1213 " is set and returns the variable in parentheses.\n"
1214 " example return code: 200 result=1 (testvariable)\n";
1216 static char usage_getvariablefull[] =
1217 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
1218 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
1219 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
1220 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
1221 " example return code: 200 result=1 (testvariable)\n";
1223 static char usage_setvariable[] =
1224 " Usage: SET VARIABLE <variablename> <value>\n";
1226 static char usage_channelstatus[] =
1227 " Usage: CHANNEL STATUS [<channelname>]\n"
1228 " Returns the status of the specified channel.\n"
1229 " If no channel name is given the returns the status of the\n"
1230 " current channel. Return values:\n"
1231 " 0 Channel is down and available\n"
1232 " 1 Channel is down, but reserved\n"
1233 " 2 Channel is off hook\n"
1234 " 3 Digits (or equivalent) have been dialed\n"
1235 " 4 Line is ringing\n"
1236 " 5 Remote end is ringing\n"
1238 " 7 Line is busy\n";
1240 static char usage_setcallerid[] =
1241 " Usage: SET CALLERID <number>\n"
1242 " Changes the callerid of the current channel.\n";
1244 static char usage_exec[] =
1245 " Usage: EXEC <application> <options>\n"
1246 " Executes <application> with given <options>.\n"
1247 " Returns whatever the application returns, or -2 on failure to find application\n";
1249 static char usage_hangup[] =
1250 " Usage: HANGUP [<channelname>]\n"
1251 " Hangs up the specified channel.\n"
1252 " If no channel name is given, hangs up the current channel\n";
1254 static char usage_answer[] =
1256 " Answers channel if not already in answer state. Returns -1 on\n"
1257 " channel failure, or 0 if successful.\n";
1259 static char usage_waitfordigit[] =
1260 " Usage: WAIT FOR DIGIT <timeout>\n"
1261 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
1262 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
1263 " the numerical value of the ascii of the digit if one is received. Use -1\n"
1264 " for the timeout value if you desire the call to block indefinitely.\n";
1266 static char usage_sendtext[] =
1267 " Usage: SEND TEXT \"<text to send>\"\n"
1268 " Sends the given text on a channel. Most channels do not support the\n"
1269 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
1270 " support text transmission. Returns -1 only on error/hangup. Text\n"
1271 " consisting of greater than one word should be placed in quotes since the\n"
1272 " command only accepts a single argument.\n";
1274 static char usage_recvchar[] =
1275 " Usage: RECEIVE CHAR <timeout>\n"
1276 " Receives a character of text on a channel. Specify timeout to be the\n"
1277 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
1278 " do not support the reception of text. Returns the decimal value of the character\n"
1279 " if one is received, or 0 if the channel does not support text reception. Returns\n"
1280 " -1 only on error/hangup.\n";
1282 static char usage_tddmode[] =
1283 " Usage: TDD MODE <on|off>\n"
1284 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
1285 " successful, or 0 if channel is not TDD-capable.\n";
1287 static char usage_sendimage[] =
1288 " Usage: SEND IMAGE <image>\n"
1289 " Sends the given image on a channel. Most channels do not support the\n"
1290 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
1291 " support image transmission. Returns -1 only on error/hangup. Image names\n"
1292 " should not include extensions.\n";
1294 static char usage_streamfile[] =
1295 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1296 " Send the given file, allowing playback to be interrupted by the given\n"
1297 " digits, if any. Use double quotes for the digits if you wish none to be\n"
1298 " permitted. If sample offset is provided then the audio will seek to sample\n"
1299 " offset before play starts. Returns 0 if playback completes without a digit\n"
1300 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1301 " or -1 on error or if the channel was disconnected. Remember, the file\n"
1302 " extension must not be included in the filename.\n";
1304 static char usage_getoption[] =
1305 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
1306 " Behaves similar to STREAM FILE but used with a timeout option.\n";
1308 static char usage_saynumber[] =
1309 " Usage: SAY NUMBER <number> <escape digits>\n"
1310 " Say a given number, returning early if any of the given DTMF digits\n"
1311 " are received on the channel. Returns 0 if playback completes without a digit\n"
1312 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1313 " -1 on error/hangup.\n";
1315 static char usage_saydigits[] =
1316 " Usage: SAY DIGITS <number> <escape digits>\n"
1317 " Say a given digit string, returning early if any of the given DTMF digits\n"
1318 " are received on the channel. Returns 0 if playback completes without a digit\n"
1319 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1320 " -1 on error/hangup.\n";
1322 static char usage_sayalpha[] =
1323 " Usage: SAY ALPHA <number> <escape digits>\n"
1324 " Say a given character string, returning early if any of the given DTMF digits\n"
1325 " are received on the channel. Returns 0 if playback completes without a digit\n"
1326 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1327 " -1 on error/hangup.\n";
1329 static char usage_saytime[] =
1330 " Usage: SAY TIME <time> <escape digits>\n"
1331 " Say a given time, returning early if any of the given DTMF digits are\n"
1332 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
1333 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
1334 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1335 " digit if one was pressed or -1 on error/hangup.\n";
1337 static char usage_sayphonetic[] =
1338 " Usage: SAY PHONETIC <string> <escape digits>\n"
1339 " Say a given character string with phonetics, returning early if any of the\n"
1340 " given DTMF digits are received on the channel. Returns 0 if playback\n"
1341 " completes without a digit pressed, the ASCII numerical value of the digit\n"
1342 " if one was pressed, or -1 on error/hangup.\n";
1344 static char usage_getdata[] =
1345 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1346 " Stream the given file, and recieve DTMF data. Returns the digits received\n"
1347 "from the channel at the other end.\n";
1349 static char usage_setcontext[] =
1350 " Usage: SET CONTEXT <desired context>\n"
1351 " Sets the context for continuation upon exiting the application.\n";
1353 static char usage_setextension[] =
1354 " Usage: SET EXTENSION <new extension>\n"
1355 " Changes the extension for continuation upon exiting the application.\n";
1357 static char usage_setpriority[] =
1358 " Usage: SET PRIORITY <num>\n"
1359 " Changes the priority for continuation upon exiting the application.\n";
1361 static char usage_recordfile[] =
1362 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
1363 " [offset samples] [BEEP] [s=silence]\n"
1364 " Record to a file until a given dtmf digit in the sequence is received\n"
1365 " Returns -1 on hangup or error. The format will specify what kind of file\n"
1366 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
1367 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
1368 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
1369 " of seconds of silence allowed before the function returns despite the\n"
1370 " lack of dtmf digits or reaching timeout. Silence value must be\n"
1371 " preceeded by \"s=\" and is also optional.\n";
1373 static char usage_autohangup[] =
1374 " Usage: SET AUTOHANGUP <time>\n"
1375 " Cause the channel to automatically hangup at <time> seconds in the\n"
1376 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
1377 " cause the autohangup feature to be disabled on this channel.\n";
1379 static char usage_noop[] =
1383 static agi_command commands[MAX_COMMANDS] = {
1384 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
1385 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1386 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1387 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1388 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1389 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1390 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1391 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
1392 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
1393 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
1394 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1395 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1396 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1397 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1398 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1399 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
1400 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1401 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1402 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
1403 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1404 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1405 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1406 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1407 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1408 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1409 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1410 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
1411 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
1412 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1413 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1414 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
1415 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1416 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1419 static void join(char *s, size_t len, char *w[])
1422 /* Join words into a string */
1427 for (x=0;w[x];x++) {
1429 strncat(s, " ", len - strlen(s) - 1);
1430 strncat(s, w[x], len - strlen(s) - 1);
1434 static int help_workhorse(int fd, char *match[])
1439 struct agi_command *e;
1441 join(matchstr, sizeof(matchstr), match);
1442 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1443 if (!commands[x].cmda[0]) break;
1446 join(fullcmd, sizeof(fullcmd), e->cmda);
1447 /* Hide commands that start with '_' */
1448 if (fullcmd[0] == '_')
1451 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1455 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
1460 int agi_register(agi_command *agi)
1463 for (x=0;x<MAX_COMMANDS - 1;x++) {
1464 if (commands[x].cmda[0] == agi->cmda[0]) {
1465 ast_log(LOG_WARNING, "Command already registered!\n");
1469 for (x=0;x<MAX_COMMANDS - 1;x++) {
1470 if (!commands[x].cmda[0]) {
1475 ast_log(LOG_WARNING, "No more room for new commands!\n");
1479 void agi_unregister(agi_command *agi)
1482 for (x=0;x<MAX_COMMANDS - 1;x++) {
1483 if (commands[x].cmda[0] == agi->cmda[0]) {
1484 memset(&commands[x], 0, sizeof(agi_command));
1489 static agi_command *find_command(char *cmds[], int exact)
1494 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1495 if (!commands[x].cmda[0]) break;
1496 /* start optimistic */
1498 for (y=0;match && cmds[y]; y++) {
1499 /* If there are no more words in the command (and we're looking for
1500 an exact match) or there is a difference between the two words,
1501 then this is not a match */
1502 if (!commands[x].cmda[y] && !exact)
1504 /* don't segfault if the next part of a command doesn't exist */
1505 if (!commands[x].cmda[y]) return NULL;
1506 if (strcasecmp(commands[x].cmda[y], cmds[y]))
1509 /* If more words are needed to complete the command then this is not
1510 a candidate (unless we're looking for a really inexact answer */
1511 if ((exact > -1) && commands[x].cmda[y])
1514 return &commands[x];
1520 static int parse_args(char *s, int *max, char *argv[])
1532 /* If it's escaped, put a literal quote */
1537 if (quoted && whitespace) {
1538 /* If we're starting a quote, coming off white space start a new word, too */
1546 if (!quoted && !escaped) {
1547 /* If we're not quoted, mark this as whitespace, and
1548 end the previous argument */
1552 /* Otherwise, just treat it as anything else */
1556 /* If we're escaped, print a literal, otherwise enable escaping */
1566 if (x >= MAX_ARGS -1) {
1567 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1570 /* Coming off of whitespace, start the next argument */
1579 /* Null terminate */
1586 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1588 char *argv[MAX_ARGS];
1593 parse_args(buf, &argc, argv);
1596 for (x=0;x<argc;x++)
1597 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1599 c = find_command(argv, 0);
1601 res = c->handler(chan, agi, argc, argv);
1603 case RESULT_SHOWUSAGE:
1604 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
1605 fdprintf(agi->fd, c->usage);
1606 fdprintf(agi->fd, "520 End of proper usage.\n");
1608 case AST_PBX_KEEPALIVE:
1609 /* We've been asked to keep alive, so do so */
1610 return AST_PBX_KEEPALIVE;
1612 case RESULT_FAILURE:
1613 /* They've already given the failure. We've been hung up on so handle this
1618 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1623 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1625 struct ast_channel *c;
1628 int returnstatus = 0;
1629 struct ast_frame *f;
1632 /* how many times we'll retry if ast_waitfor_nandfs will return without either
1633 channel or file descriptor in case select is interrupted by a system call (EINTR) */
1636 if (!(readf = fdopen(agi->ctrl, "r"))) {
1637 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1644 setup_env(chan, request, agi->fd, (agi->audio > -1));
1647 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1650 /* Idle the channel until we get a command */
1653 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1657 /* If it's voice, write it to the audio pipe */
1658 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1659 /* Write, ignoring errors */
1660 write(agi->audio, f->data, f->datalen);
1664 } else if (outfd > -1) {
1666 if (!fgets(buf, sizeof(buf), readf)) {
1667 /* Program terminated */
1670 if (option_verbose > 2)
1671 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1672 /* No need to kill the pid anymore, since they closed us */
1676 /* get rid of trailing newline, if any */
1677 if (*buf && buf[strlen(buf) - 1] == '\n')
1678 buf[strlen(buf) - 1] = 0;
1680 ast_verbose("AGI Rx << %s\n", buf);
1681 returnstatus |= agi_handle_command(chan, agi, buf);
1682 /* If the handle_command returns -1, we need to stop */
1683 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1688 ast_log(LOG_WARNING, "No channel, no fd?\n");
1694 /* Notify process */
1698 return returnstatus;
1701 static int handle_showagi(int fd, int argc, char *argv[]) {
1702 struct agi_command *e;
1705 return RESULT_SHOWUSAGE;
1707 e = find_command(argv + 2, 1);
1709 ast_cli(fd, e->usage);
1711 if (find_command(argv + 2, -1)) {
1712 return help_workhorse(fd, argv + 1);
1714 join(fullcmd, sizeof(fullcmd), argv+1);
1715 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1719 return help_workhorse(fd, NULL);
1721 return RESULT_SUCCESS;
1724 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1725 struct agi_command *e;
1732 return RESULT_SHOWUSAGE;
1734 if (!(htmlfile = fopen(argv[2], "wt"))) {
1735 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1736 return RESULT_SHOWUSAGE;
1739 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1740 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1743 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1745 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1747 if (!commands[x].cmda[0]) break;
1750 join(fullcmd, sizeof(fullcmd), e->cmda);
1751 /* Hide commands that start with '_' */
1752 if (fullcmd[0] == '_')
1755 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1756 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1760 tempstr = strsep(&stringp, "\n");
1762 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1764 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1765 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1766 fprintf(htmlfile, "%s<BR>\n",tempstr);
1769 fprintf(htmlfile, "</TD></TR>\n");
1770 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1774 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1776 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1777 return RESULT_SUCCESS;
1780 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1783 struct localuser *u;
1784 char *argv[MAX_ARGS];
1786 char *tmp = (char *)buf;
1793 if (!data || ast_strlen_zero(data)) {
1794 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1797 strncpy(buf, data, sizeof(buf) - 1);
1799 memset(&agi, 0, sizeof(agi));
1800 while ((stringp = strsep(&tmp, "|"))) {
1801 argv[argc++] = stringp;
1807 /* Answer if need be */
1808 if (chan->_state != AST_STATE_UP) {
1809 if (ast_answer(chan)) {
1810 LOCAL_USER_REMOVE(u);
1815 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
1820 res = run_agi(chan, argv[0], &agi, pid, dead);
1825 LOCAL_USER_REMOVE(u);
1829 static int agi_exec(struct ast_channel *chan, void *data)
1831 if (chan->_softhangup)
1832 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1833 return agi_exec_full(chan, data, 0, 0);
1836 static int eagi_exec(struct ast_channel *chan, void *data)
1840 if (chan->_softhangup)
1841 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
1842 readformat = chan->readformat;
1843 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1844 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1847 res = agi_exec_full(chan, data, 1, 0);
1849 if (ast_set_read_format(chan, readformat)) {
1850 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1856 static int deadagi_exec(struct ast_channel *chan, void *data)
1858 return agi_exec_full(chan, data, 0, 1);
1861 static char showagi_help[] =
1862 "Usage: show agi [topic]\n"
1863 " When called with a topic as an argument, displays usage\n"
1864 " information on the given command. If called without a\n"
1865 " topic, it provides a list of AGI commands.\n";
1868 static char dumpagihtml_help[] =
1869 "Usage: dump agihtml <filename>\n"
1870 " Dumps the agi command list in html format to given filename\n";
1872 static struct ast_cli_entry showagi =
1873 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1875 static struct ast_cli_entry dumpagihtml =
1876 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1878 int unload_module(void)
1880 STANDARD_HANGUP_LOCALUSERS;
1881 ast_cli_unregister(&showagi);
1882 ast_cli_unregister(&dumpagihtml);
1883 ast_cli_unregister(&cli_debug);
1884 ast_cli_unregister(&cli_no_debug);
1885 ast_unregister_application(eapp);
1886 ast_unregister_application(deadapp);
1887 return ast_unregister_application(app);
1890 int load_module(void)
1892 ast_cli_register(&showagi);
1893 ast_cli_register(&dumpagihtml);
1894 ast_cli_register(&cli_debug);
1895 ast_cli_register(&cli_no_debug);
1896 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
1897 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1898 return ast_register_application(app, agi_exec, synopsis, descrip);
1901 char *description(void)
1909 STANDARD_USECOUNT(res);
1915 return ASTERISK_GPL_KEY;