2 * Asterisk -- A telephony toolkit for Linux.
4 * Asterisk Gateway Interface
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/module.h>
24 #include <sys/signal.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/logger.h>
28 #include <asterisk/options.h>
29 #include <asterisk/image.h>
30 #include <asterisk/say.h>
31 #include "../asterisk.h"
37 /* Recycle some stuff from the CLI interface */
38 #define fdprintf ast_cli
40 typedef struct agi_command {
41 /* Null terminated list of the words of the command */
42 char *cmda[AST_MAX_CMD_LEN];
43 /* Handler for the command (fd for output, # of arguments, argument list).
44 Returns RESULT_SHOWUSAGE for improper arguments */
45 int (*handler)(struct ast_channel *chan, int fd, int argc, char *argv[]);
46 /* Summary of the command (< 60 characters) */
48 /* Detailed usage information */
52 static char *tdesc = "Asterisk Gateway Interface (AGI)";
54 static char *app = "AGI";
56 static char *synopsis = "Executes an AGI compliant application";
58 static char *descrip =
59 " AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
60 "program on a channel. AGI allows Asterisk to launch external programs\n"
61 "written in any language to control a telephony channel, play audio,\n"
62 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
63 "and stdout. Returns -1 on hangup or if application requested hangup, or\n"
64 "0 on non-hangup exit.\n";
70 #define TONE_BLOCK_SIZE 200
72 static float loudness = 8192.0;
74 unsigned char linear2ulaw(short sample);
75 static void make_tone_block(unsigned char *data, float f1, int *x);
77 static void make_tone_block(unsigned char *data, float f1, int *x)
82 for(i = 0; i < TONE_BLOCK_SIZE; i++)
84 val = loudness * sin((f1 * 2.0 * M_PI * (*x)++)/8000.0);
85 data[i] = linear2ulaw((int)val);
87 /* wrap back around from 8000 */
88 if (*x >= 8000) *x = 0;
92 static int launch_script(char *script, char *args, int *fds, int *opid)
99 if (script[0] != '/') {
100 snprintf(tmp, sizeof(tmp), "%s/%s", AST_AGI_DIR, script);
104 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
108 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
115 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
119 /* Redirect stdin and out */
120 dup2(fromast[0], STDIN_FILENO);
121 dup2(toast[1], STDOUT_FILENO);
122 /* Close everything but stdin/out/error */
123 for (x=STDERR_FILENO + 1;x<1024;x++)
126 execl(script, script, args, NULL);
127 ast_log(LOG_WARNING, "Failed to execute '%s': %s\n", script, strerror(errno));
130 if (option_verbose > 2)
131 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
134 /* close what we're not using in the parent */
142 static void setup_env(struct ast_channel *chan, char *request, int fd)
144 /* Print initial environment, with agi_request always being the first
146 fdprintf(fd, "agi_request: %s\n", request);
147 fdprintf(fd, "agi_channel: %s\n", chan->name);
148 fdprintf(fd, "agi_language: %s\n", chan->language);
149 fdprintf(fd, "agi_type: %s\n", chan->type);
152 fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "");
153 fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "");
155 /* Context information */
156 fdprintf(fd, "agi_context: %s\n", chan->context);
157 fdprintf(fd, "agi_extension: %s\n", chan->exten);
158 fdprintf(fd, "agi_priority: %d\n", chan->priority);
160 /* End with empty return */
164 static int handle_waitfordigit(struct ast_channel *chan, int fd, int argc, char *argv[])
169 return RESULT_SHOWUSAGE;
170 if (sscanf(argv[3], "%i", &to) != 1)
171 return RESULT_SHOWUSAGE;
172 res = ast_waitfordigit(chan, to);
173 fdprintf(fd, "200 result=%d\n", res);
175 return RESULT_SUCCESS;
177 return RESULT_FAILURE;
180 static int handle_sendtext(struct ast_channel *chan, int fd, int argc, char *argv[])
184 return RESULT_SHOWUSAGE;
185 res = ast_sendtext(chan, argv[2]);
186 fdprintf(fd, "200 result=%d\n", res);
188 return RESULT_SUCCESS;
190 return RESULT_FAILURE;
193 static int handle_sendimage(struct ast_channel *chan, int fd, int argc, char *argv[])
197 return RESULT_SHOWUSAGE;
198 res = ast_send_image(chan, argv[2]);
199 if (!chan->softhangup)
201 fdprintf(fd, "200 result=%d\n", res);
203 return RESULT_SUCCESS;
205 return RESULT_FAILURE;
208 static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *argv[])
212 return RESULT_SHOWUSAGE;
213 res = ast_streamfile(chan, argv[2],chan->language);
215 fdprintf(fd, "200 result=%d\n", res);
217 return RESULT_SHOWUSAGE;
219 return RESULT_FAILURE;
221 res = ast_waitstream(chan, argv[3]);
223 fdprintf(fd, "200 result=%d\n", res);
225 return RESULT_SUCCESS;
227 return RESULT_FAILURE;
230 static int handle_saynumber(struct ast_channel *chan, int fd, int argc, char *argv[])
235 return RESULT_SHOWUSAGE;
236 if (sscanf(argv[2], "%i", &num) != 1)
237 return RESULT_SHOWUSAGE;
238 res = ast_say_number(chan, num, chan->language);
239 fdprintf(fd, "200 result=%d\n", res);
241 return RESULT_SUCCESS;
243 return RESULT_FAILURE;
246 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout);
248 static int handle_getdata(struct ast_channel *chan, int fd, int argc, char *argv[])
256 return RESULT_SHOWUSAGE;
257 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
258 if (argc >= 5) max = atoi(argv[4]); else max = 50;
259 res = ast_app_getdata(chan, argv[2], data, max, timeout);
261 fdprintf(fd, "200 result=%s (timeout)\n", data);
263 fdprintf(fd, "200 result=%s\n", data);
265 return RESULT_SUCCESS;
267 return RESULT_FAILURE;
270 static int handle_setcontext(struct ast_channel *chan, int fd, int argc, char *argv[])
274 return RESULT_SHOWUSAGE;
275 strncpy(chan->context, argv[2], sizeof(chan->context));
276 fdprintf(fd, "200 result=0\n");
277 return RESULT_SUCCESS;
280 static int handle_setextension(struct ast_channel *chan, int fd, int argc, char **argv)
283 return RESULT_SHOWUSAGE;
284 strncpy(chan->exten, argv[2], sizeof(chan->exten));
285 fdprintf(fd, "200 result=0\n");
286 return RESULT_SUCCESS;
289 static int handle_setpriority(struct ast_channel *chan, int fd, int argc, char **argv)
293 return RESULT_SHOWUSAGE;
294 if (sscanf(argv[2], "%i", &pri) != 1)
295 return RESULT_SHOWUSAGE;
296 chan->priority = pri - 1;
297 fdprintf(fd, "200 result=0\n");
298 return RESULT_SUCCESS;
301 static int ms_diff(struct timeval *tv1, struct timeval *tv2)
305 ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
306 ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
310 static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *argv[])
312 struct ast_filestream *fs;
313 struct ast_frame *f,wf;
314 struct timeval tv, start, lastout, now, notime = { 0,0 } ;
316 unsigned char tone_block[TONE_BLOCK_SIZE];
321 return RESULT_SHOWUSAGE;
322 if (sscanf(argv[5], "%i", &ms) != 1)
323 return RESULT_SHOWUSAGE;
325 if (argc > 6) { /* if to beep */
327 lastout.tv_sec = lastout.tv_usec = 0;
328 for(j = 0; j < 13; j++)
330 do gettimeofday(&now,NULL);
331 while (lastout.tv_sec &&
332 (ms_diff(&now,&lastout) < 25));
333 lastout.tv_sec = now.tv_sec;
334 lastout.tv_usec = now.tv_usec;
335 wf.frametype = AST_FRAME_VOICE;
336 wf.subclass = AST_FORMAT_ULAW;
337 wf.offset = AST_FRIENDLY_OFFSET;
339 wf.data = tone_block;
340 wf.datalen = TONE_BLOCK_SIZE;
341 /* make this tone block */
342 make_tone_block(tone_block,1000.0,&i);
343 wf.timelen = wf.datalen / 8;
344 if (ast_write(chan, &wf)) {
345 fdprintf(fd, "200 result=%d (hangup)\n", 0);
346 return RESULT_FAILURE;
349 FD_SET(chan->fds[0],&readfds);
350 /* if no read avail, do send again */
351 if (select(chan->fds[0] + 1,&readfds,NULL,
352 NULL,¬ime) < 1) continue;
355 fdprintf(fd, "200 result=%d (hangup)\n", 0);
356 return RESULT_FAILURE;
358 switch(f->frametype) {
360 if (strchr(argv[4], f->subclass)) {
361 /* This is an interrupting chracter */
362 fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
364 return RESULT_SUCCESS;
367 case AST_FRAME_VOICE:
368 break; /* throw it away */
372 /* suck in 5 voice frames to make up for echo of beep, etc */
373 for(i = 0; i < 5; i++) {
376 fdprintf(fd, "200 result=%d (hangup)\n", 0);
377 return RESULT_FAILURE;
379 switch(f->frametype) {
381 if (strchr(argv[4], f->subclass)) {
382 /* This is an interrupting chracter */
383 fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
385 return RESULT_SUCCESS;
388 case AST_FRAME_VOICE:
389 break; /* throw it away */
396 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_TRUNC | O_WRONLY, 0, 0644);
398 fdprintf(fd, "200 result=%d (writefile)\n", res);
399 return RESULT_FAILURE;
401 gettimeofday(&start, NULL);
402 gettimeofday(&tv, NULL);
403 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
404 res = ast_waitfor(chan, -1);
407 fdprintf(fd, "200 result=%d (waitfor)\n", res);
408 return RESULT_FAILURE;
412 fdprintf(fd, "200 result=%d (hangup)\n", 0);
414 return RESULT_FAILURE;
416 switch(f->frametype) {
418 if (strchr(argv[4], f->subclass)) {
419 /* This is an interrupting chracter */
420 fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
423 return RESULT_SUCCESS;
426 case AST_FRAME_VOICE:
427 ast_writestream(fs, f);
431 gettimeofday(&tv, NULL);
433 fdprintf(fd, "200 result=%d (timeout)\n", 0);
435 return RESULT_SUCCESS;
438 static char usage_waitfordigit[] =
439 " Usage: WAIT FOR DIGIT <timeout>\n"
440 " Waits up to 'timeout' seconds for channel to receive a DTMF digit.\n"
441 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
442 " the numerical value of the ascii of the digit if one is received. Use -1\n"
443 " for the timeout value if you desire the call to block indefinitely.\n";
445 static char usage_sendtext[] =
446 " Usage: SEND TEXT \"<text to send>\"\n"
447 " Sends the given text on a channel. Most channels do not support the\n"
448 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
449 " support text transmission. Returns -1 only on error/hangup. Text\n"
450 " consisting of greater than one word should be placed in quotes since the\n"
451 " command only accepts a single argument.\n";
453 static char usage_sendimage[] =
454 " Usage: SEND IMAGE <image>\n"
455 " Sends the given image on a channel. Most channels do not support the\n"
456 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
457 " support image transmission. Returns -1 only on error/hangup. Image names\n"
458 " should not include extensions.\n";
460 static char usage_streamfile[] =
461 " Usage: STREAM FILE <filename> <escape digits>\n"
462 " Send the given file, allowing playback to be interrupted by the given\n"
463 " digits, if any. Use double quotes for the digits if you wish none to be\n"
464 " permitted. Returns 0 if playback completes without a digit being pressed, or\n"
465 " the ASCII numerical value of the digit if one was pressed, or -1 on error or\n"
466 " if the channel was disconnected. Remember, the file extension must not be\n"
467 " included in the filename.\n";
469 static char usage_saynumber[] =
470 " Usage: SAY NUMBER <number> <escape digits>\n"
471 " Say a given number, returning early if any of the given DTMF digits\n"
472 " are received on the channel. Returns 0 if playback completes without a digit\n"
473 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
474 " -1 on error/hangup.\n";
476 static char usage_getdata[] =
477 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
478 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
479 "from the channel at the other end.\n";
481 static char usage_setcontext[] =
482 " Usage: SET CONTEXT <desired context>\n"
483 " Sets the context for continuation upon exiting the application.\n";
485 static char usage_setextension[] =
486 " Usage: SET EXTENSION <new extension>\n"
487 " Changes the extension for continuation upon exiting the application.\n";
489 static char usage_setpriority[] =
490 " Usage: SET PRIORITY <num>\n"
491 " Changes the priority for continuation upon exiting the application.\n";
493 static char usage_recordfile[] =
494 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [BEEP]\n"
495 " Record to a file until a given dtmf digit in the sequence is received\n"
496 " Returns -1 on hangup or error. The format will specify what kind of file\n"
497 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
498 " -1 for no timeout\n";
500 agi_command commands[] = {
501 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
502 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
503 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
504 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
505 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
506 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
507 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
508 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
509 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
510 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile }
513 static agi_command *find_command(char *cmds[])
518 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
519 /* start optimistic */
521 for (y=0;match && cmds[y]; y++) {
522 /* If there are no more words in the command (and we're looking for
523 an exact match) or there is a difference between the two words,
524 then this is not a match */
525 if (!commands[x].cmda[y])
527 if (strcasecmp(commands[x].cmda[y], cmds[y]))
530 /* If more words are needed to complete the command then this is not
531 a candidate (unless we're looking for a really inexact answer */
532 if (commands[x].cmda[y])
541 static int parse_args(char *s, int *max, char *argv[])
553 /* If it's escaped, put a literal quote */
558 if (quoted && whitespace) {
559 /* If we're starting a quote, coming off white space start a new word, too */
567 if (!quoted && !escaped) {
568 /* If we're not quoted, mark this as whitespace, and
569 end the previous argument */
573 /* Otherwise, just treat it as anything else */
577 /* If we're escaped, print a literal, otherwise enable escaping */
587 if (x >= MAX_ARGS -1) {
588 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
591 /* Coming off of whitespace, start the next argument */
607 static int agi_handle_command(struct ast_channel *chan, int fd, char *buf)
609 char *argv[MAX_ARGS];
614 parse_args(buf, &argc, argv);
618 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
620 c = find_command(argv);
622 res = c->handler(chan, fd, argc, argv);
624 case RESULT_SHOWUSAGE:
625 fdprintf(fd, "520-Invalid command syntax. Proper usage follows:\n");
626 fdprintf(fd, c->usage);
627 fdprintf(fd, "520 End of proper usage.\n");
630 /* They've already given the failure. We've been hung up on so handle this
635 fdprintf(fd, "510 Invalid or unknown command\n");
640 static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid)
642 struct ast_channel *c;
645 int returnstatus = 0;
649 if (!(readf = fdopen(fds[0], "r"))) {
650 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
655 setup_env(chan, request, fds[1]);
658 c = ast_waitfor_nandfds(&chan, 1, &fds[0], 1, NULL, &outfd, &ms);
660 /* Idle the channel until we get a command */
663 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
669 } else if (outfd > -1) {
670 if (!fgets(buf, sizeof(buf), readf)) {
671 /* Program terminated */
674 if (option_verbose > 2)
675 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
676 /* No need to kill the pid anymore, since they closed us */
680 returnstatus |= agi_handle_command(chan, fds[1], buf);
681 /* If the handle_command returns -1, we need to stop */
682 if (returnstatus < 0) {
686 ast_log(LOG_WARNING, "No channel, no fd?\n");
698 static int agi_exec(struct ast_channel *chan, void *data)
706 if (!data || !strlen(data)) {
707 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
712 strncpy(tmp, data, sizeof(tmp));
714 args = strtok(NULL, "|");
715 ringy = strtok(NULL,"|");
719 /* Answer if need be */
720 if (chan->state != AST_STATE_UP) {
721 if (ringy) { /* if for ringing first */
722 /* a little ringy-dingy first */
723 ast_indicate(chan, AST_CONTROL_RINGING);
726 if (ast_answer(chan)) {
727 LOCAL_USER_REMOVE(u);
731 res = launch_script(tmp, args, fds, &pid);
733 res = run_agi(chan, tmp, fds, pid);
737 LOCAL_USER_REMOVE(u);
741 int unload_module(void)
743 STANDARD_HANGUP_LOCALUSERS;
744 return ast_unregister_application(app);
747 int load_module(void)
749 return ast_register_application(app, agi_exec, synopsis, descrip);
752 char *description(void)
760 STANDARD_USECOUNT(res);
766 return ASTERISK_GPL_KEY;
775 static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
776 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
777 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
778 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
779 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
780 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
781 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
782 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
783 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
784 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
785 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
786 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
787 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
788 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
789 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
790 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
791 int sign, exponent, mantissa;
792 unsigned char ulawbyte;
794 /* Get the sample into sign-magnitude. */
795 sign = (sample >> 8) & 0x80; /* set aside the sign */
796 if (sign != 0) sample = -sample; /* get magnitude */
797 if (sample > CLIP) sample = CLIP; /* clip the magnitude */
799 /* Convert from 16 bit linear to ulaw. */
800 sample = sample + BIAS;
801 exponent = exp_lut[(sample >> 7) & 0xFF];
802 mantissa = (sample >> (exponent + 3)) & 0x0F;
803 ulawbyte = ~(sign | (exponent << 4) | mantissa);
805 if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */