mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	Version 0.1.9 from FTP
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@360 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										809
									
								
								apps/app_agi.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										809
									
								
								apps/app_agi.c
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,809 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Asterisk -- A telephony toolkit for Linux.
 | 
			
		||||
 *
 | 
			
		||||
 * Asterisk Gateway Interface
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (C) 1999, Mark Spencer
 | 
			
		||||
 *
 | 
			
		||||
 * Mark Spencer <markster@linux-support.net>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software, distributed under the terms of
 | 
			
		||||
 * the GNU General Public License
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <asterisk/file.h>
 | 
			
		||||
#include <asterisk/logger.h>
 | 
			
		||||
#include <asterisk/channel.h>
 | 
			
		||||
#include <asterisk/pbx.h>
 | 
			
		||||
#include <asterisk/module.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/signal.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <asterisk/cli.h>
 | 
			
		||||
#include <asterisk/logger.h>
 | 
			
		||||
#include <asterisk/options.h>
 | 
			
		||||
#include <asterisk/image.h>
 | 
			
		||||
#include <asterisk/say.h>
 | 
			
		||||
#include "../asterisk.h"
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_ARGS 128
 | 
			
		||||
 | 
			
		||||
/* Recycle some stuff from the CLI interface */
 | 
			
		||||
#define fdprintf ast_cli
 | 
			
		||||
 | 
			
		||||
typedef struct agi_command {
 | 
			
		||||
	/* Null terminated list of the words of the command */
 | 
			
		||||
	char *cmda[AST_MAX_CMD_LEN];
 | 
			
		||||
	/* Handler for the command (fd for output, # of arguments, argument list). 
 | 
			
		||||
	    Returns RESULT_SHOWUSAGE for improper arguments */
 | 
			
		||||
	int (*handler)(struct ast_channel *chan, int fd, int argc, char *argv[]);
 | 
			
		||||
	/* Summary of the command (< 60 characters) */
 | 
			
		||||
	char *summary;
 | 
			
		||||
	/* Detailed usage information */
 | 
			
		||||
	char *usage;
 | 
			
		||||
} agi_command;
 | 
			
		||||
 | 
			
		||||
static char *tdesc = "Asterisk Gateway Interface (AGI)";
 | 
			
		||||
 | 
			
		||||
static char *app = "AGI";
 | 
			
		||||
 | 
			
		||||
static char *synopsis = "Executes an AGI compliant application";
 | 
			
		||||
 | 
			
		||||
static char *descrip =
 | 
			
		||||
"  AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
 | 
			
		||||
"program on a channel.   AGI allows Asterisk to launch external programs\n"
 | 
			
		||||
"written in any language to control a telephony channel, play audio,\n"
 | 
			
		||||
"read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
 | 
			
		||||
"and stdout.  Returns -1 on hangup or if application requested hangup, or\n"
 | 
			
		||||
"0 on non-hangup exit.\n";
 | 
			
		||||
 | 
			
		||||
STANDARD_LOCAL_USER;
 | 
			
		||||
 | 
			
		||||
LOCAL_USER_DECL;
 | 
			
		||||
 | 
			
		||||
#define TONE_BLOCK_SIZE 200
 | 
			
		||||
 | 
			
		||||
static float loudness = 8192.0;
 | 
			
		||||
 | 
			
		||||
unsigned char linear2ulaw(short sample);
 | 
			
		||||
static void make_tone_block(unsigned char *data, float f1, int *x);
 | 
			
		||||
 | 
			
		||||
static void make_tone_block(unsigned char *data, float f1, int *x)
 | 
			
		||||
{
 | 
			
		||||
int	i;
 | 
			
		||||
float	val;
 | 
			
		||||
 | 
			
		||||
	for(i = 0; i < TONE_BLOCK_SIZE; i++)
 | 
			
		||||
	{
 | 
			
		||||
		val = loudness * sin((f1 * 2.0 * M_PI * (*x)++)/8000.0);
 | 
			
		||||
		data[i] = linear2ulaw((int)val);
 | 
			
		||||
	 }		
 | 
			
		||||
	  /* wrap back around from 8000 */
 | 
			
		||||
	if (*x >= 8000) *x = 0;
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int launch_script(char *script, char *args, int *fds, int *opid)
 | 
			
		||||
{
 | 
			
		||||
	char tmp[256];
 | 
			
		||||
	int pid;
 | 
			
		||||
	int toast[2];
 | 
			
		||||
	int fromast[2];
 | 
			
		||||
	int x;
 | 
			
		||||
	if (script[0] != '/') {
 | 
			
		||||
		snprintf(tmp, sizeof(tmp), "%s/%s", AST_AGI_DIR, script);
 | 
			
		||||
		script = tmp;
 | 
			
		||||
	}
 | 
			
		||||
	if (pipe(toast)) {
 | 
			
		||||
		ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (pipe(fromast)) {
 | 
			
		||||
		ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
 | 
			
		||||
		close(toast[0]);
 | 
			
		||||
		close(toast[1]);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	pid = fork();
 | 
			
		||||
	if (pid < 0) {
 | 
			
		||||
		ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!pid) {
 | 
			
		||||
		/* Redirect stdin and out */
 | 
			
		||||
		dup2(fromast[0], STDIN_FILENO);
 | 
			
		||||
		dup2(toast[1], STDOUT_FILENO);
 | 
			
		||||
		/* Close everything but stdin/out/error */
 | 
			
		||||
		for (x=STDERR_FILENO + 1;x<1024;x++) 
 | 
			
		||||
			close(x);
 | 
			
		||||
		/* Execute script */
 | 
			
		||||
		execl(script, script, args, NULL);
 | 
			
		||||
		ast_log(LOG_WARNING, "Failed to execute '%s': %s\n", script, strerror(errno));
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
	if (option_verbose > 2) 
 | 
			
		||||
		ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
 | 
			
		||||
	fds[0] = toast[0];
 | 
			
		||||
	fds[1] = fromast[1];
 | 
			
		||||
	/* close what we're not using in the parent */
 | 
			
		||||
	close(toast[1]);
 | 
			
		||||
	close(fromast[0]);
 | 
			
		||||
	*opid = pid;
 | 
			
		||||
	return 0;
 | 
			
		||||
		
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void setup_env(struct ast_channel *chan, char *request, int fd)
 | 
			
		||||
{
 | 
			
		||||
	/* Print initial environment, with agi_request always being the first
 | 
			
		||||
	   thing */
 | 
			
		||||
	fdprintf(fd, "agi_request: %s\n", request);
 | 
			
		||||
	fdprintf(fd, "agi_channel: %s\n", chan->name);
 | 
			
		||||
	fdprintf(fd, "agi_language: %s\n", chan->language);
 | 
			
		||||
	fdprintf(fd, "agi_type: %s\n", chan->type);
 | 
			
		||||
 | 
			
		||||
	/* ANI/DNIS */
 | 
			
		||||
	fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "");
 | 
			
		||||
	fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "");
 | 
			
		||||
 | 
			
		||||
	/* Context information */
 | 
			
		||||
	fdprintf(fd, "agi_context: %s\n", chan->context);
 | 
			
		||||
	fdprintf(fd, "agi_extension: %s\n", chan->exten);
 | 
			
		||||
	fdprintf(fd, "agi_priority: %d\n", chan->priority);
 | 
			
		||||
 | 
			
		||||
	/* End with empty return */
 | 
			
		||||
	fdprintf(fd, "\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_waitfordigit(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	int to;
 | 
			
		||||
	if (argc != 4)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	if (sscanf(argv[3], "%i", &to) != 1)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	res = ast_waitfordigit(chan, to);
 | 
			
		||||
	fdprintf(fd, "200 result=%d\n", res);
 | 
			
		||||
	if (res >= 0)
 | 
			
		||||
		return RESULT_SUCCESS;
 | 
			
		||||
	else
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_sendtext(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	if (argc != 3)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	res = ast_sendtext(chan, argv[2]);
 | 
			
		||||
	fdprintf(fd, "200 result=%d\n", res);
 | 
			
		||||
	if (res >= 0)
 | 
			
		||||
		return RESULT_SUCCESS;
 | 
			
		||||
	else
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_sendimage(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	if (argc != 3)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	res = ast_send_image(chan, argv[2]);
 | 
			
		||||
	if (!chan->softhangup)
 | 
			
		||||
		res = 0;
 | 
			
		||||
	fdprintf(fd, "200 result=%d\n", res);
 | 
			
		||||
	if (res >= 0)
 | 
			
		||||
		return RESULT_SUCCESS;
 | 
			
		||||
	else
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	if (argc != 4)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	res = ast_streamfile(chan, argv[2],chan->language);
 | 
			
		||||
	if (res) {
 | 
			
		||||
		fdprintf(fd, "200 result=%d\n", res);
 | 
			
		||||
		if (res >= 0)
 | 
			
		||||
			return RESULT_SHOWUSAGE;
 | 
			
		||||
		else
 | 
			
		||||
			return RESULT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
	res = ast_waitstream(chan, argv[3]);
 | 
			
		||||
	
 | 
			
		||||
	fdprintf(fd, "200 result=%d\n", res);
 | 
			
		||||
	if (res >= 0)
 | 
			
		||||
		return RESULT_SUCCESS;
 | 
			
		||||
	else
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_saynumber(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	int num;
 | 
			
		||||
	if (argc != 4)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	if (sscanf(argv[2], "%i", &num) != 1)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	res = ast_say_number(chan, num, chan->language);
 | 
			
		||||
	fdprintf(fd, "200 result=%d\n", res);
 | 
			
		||||
	if (res >= 0)
 | 
			
		||||
		return RESULT_SUCCESS;
 | 
			
		||||
	else
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout);
 | 
			
		||||
 | 
			
		||||
static int handle_getdata(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	char data[50];
 | 
			
		||||
	int max;
 | 
			
		||||
	int timeout;
 | 
			
		||||
 | 
			
		||||
	if (argc < 3)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
 | 
			
		||||
	if (argc >= 5) max = atoi(argv[4]); else max = 50;
 | 
			
		||||
	res = ast_app_getdata(chan, argv[2], data, max, timeout);
 | 
			
		||||
	if (res == 1)
 | 
			
		||||
		fdprintf(fd, "200 result=%s (timeout)\n", data);
 | 
			
		||||
	else
 | 
			
		||||
		fdprintf(fd, "200 result=%s\n", data);
 | 
			
		||||
	if (res >= 0)
 | 
			
		||||
		return RESULT_SUCCESS;
 | 
			
		||||
	else
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_setcontext(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	if (argc != 3)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	strncpy(chan->context, argv[2], sizeof(chan->context));
 | 
			
		||||
	fdprintf(fd, "200 result=0\n");
 | 
			
		||||
	return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
static int handle_setextension(struct ast_channel *chan, int fd, int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	if (argc != 3)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	strncpy(chan->exten, argv[2], sizeof(chan->exten));
 | 
			
		||||
	fdprintf(fd, "200 result=0\n");
 | 
			
		||||
	return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_setpriority(struct ast_channel *chan, int fd, int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int pri;
 | 
			
		||||
	if (argc != 3)
 | 
			
		||||
		return RESULT_SHOWUSAGE;	
 | 
			
		||||
	if (sscanf(argv[2], "%i", &pri) != 1)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	chan->priority = pri - 1;
 | 
			
		||||
	fdprintf(fd, "200 result=0\n");
 | 
			
		||||
	return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
		
 | 
			
		||||
static int ms_diff(struct timeval *tv1, struct timeval *tv2)
 | 
			
		||||
{
 | 
			
		||||
int	ms;
 | 
			
		||||
	
 | 
			
		||||
	ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
 | 
			
		||||
	ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
 | 
			
		||||
	return(ms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	struct ast_filestream *fs;
 | 
			
		||||
	struct ast_frame *f,wf;
 | 
			
		||||
	struct timeval tv, start, lastout, now, notime = { 0,0 } ;
 | 
			
		||||
	fd_set readfds;
 | 
			
		||||
	unsigned char tone_block[TONE_BLOCK_SIZE];
 | 
			
		||||
	int res = -1;
 | 
			
		||||
	int ms,i,j;
 | 
			
		||||
 | 
			
		||||
	if (argc < 6)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
	if (sscanf(argv[5], "%i", &ms) != 1)
 | 
			
		||||
		return RESULT_SHOWUSAGE;
 | 
			
		||||
 | 
			
		||||
	if (argc > 6) { /* if to beep */
 | 
			
		||||
		i = 0;
 | 
			
		||||
		lastout.tv_sec = lastout.tv_usec = 0;
 | 
			
		||||
		for(j = 0; j < 13; j++)
 | 
			
		||||
		   {
 | 
			
		||||
			do gettimeofday(&now,NULL);
 | 
			
		||||
			while (lastout.tv_sec && 
 | 
			
		||||
				(ms_diff(&now,&lastout) < 25));
 | 
			
		||||
			lastout.tv_sec = now.tv_sec;
 | 
			
		||||
			lastout.tv_usec = now.tv_usec;
 | 
			
		||||
			wf.frametype = AST_FRAME_VOICE;
 | 
			
		||||
			wf.subclass = AST_FORMAT_ULAW;
 | 
			
		||||
			wf.offset = AST_FRIENDLY_OFFSET;
 | 
			
		||||
			wf.mallocd = 0;
 | 
			
		||||
			wf.data = tone_block;
 | 
			
		||||
			wf.datalen = TONE_BLOCK_SIZE;				
 | 
			
		||||
			/* make this tone block */
 | 
			
		||||
			make_tone_block(tone_block,1000.0,&i);
 | 
			
		||||
			wf.timelen = wf.datalen / 8;
 | 
			
		||||
		        if (ast_write(chan, &wf)) {
 | 
			
		||||
				fdprintf(fd, "200 result=%d (hangup)\n", 0);
 | 
			
		||||
				return RESULT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
			FD_ZERO(&readfds);
 | 
			
		||||
			FD_SET(chan->fds[0],&readfds);
 | 
			
		||||
			  /* if no read avail, do send again */
 | 
			
		||||
			if (select(chan->fds[0] + 1,&readfds,NULL,
 | 
			
		||||
				NULL,¬ime) < 1) continue;
 | 
			
		||||
			f = ast_read(chan);
 | 
			
		||||
			if (!f) {
 | 
			
		||||
				fdprintf(fd, "200 result=%d (hangup)\n", 0);
 | 
			
		||||
				return RESULT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
			switch(f->frametype) {
 | 
			
		||||
			case AST_FRAME_DTMF:
 | 
			
		||||
				if (strchr(argv[4], f->subclass)) {
 | 
			
		||||
					/* This is an interrupting chracter */
 | 
			
		||||
					fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
 | 
			
		||||
					ast_frfree(f);
 | 
			
		||||
					return RESULT_SUCCESS;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case AST_FRAME_VOICE:
 | 
			
		||||
				break;  /* throw it away */
 | 
			
		||||
			}
 | 
			
		||||
			ast_frfree(f);
 | 
			
		||||
		   }
 | 
			
		||||
		  /* suck in 5 voice frames to make up for echo of beep, etc */
 | 
			
		||||
		for(i = 0; i < 5; i++) {
 | 
			
		||||
			f = ast_read(chan);
 | 
			
		||||
			if (!f) {
 | 
			
		||||
				fdprintf(fd, "200 result=%d (hangup)\n", 0);
 | 
			
		||||
				return RESULT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
			switch(f->frametype) {
 | 
			
		||||
			case AST_FRAME_DTMF:
 | 
			
		||||
				if (strchr(argv[4], f->subclass)) {
 | 
			
		||||
					/* This is an interrupting chracter */
 | 
			
		||||
					fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
 | 
			
		||||
					ast_frfree(f);
 | 
			
		||||
					return RESULT_SUCCESS;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case AST_FRAME_VOICE:
 | 
			
		||||
				break;  /* throw it away */
 | 
			
		||||
			}
 | 
			
		||||
			ast_frfree(f);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_TRUNC | O_WRONLY, 0, 0644);
 | 
			
		||||
	if (!fs) {
 | 
			
		||||
		fdprintf(fd, "200 result=%d (writefile)\n", res);
 | 
			
		||||
		return RESULT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
	gettimeofday(&start, NULL);
 | 
			
		||||
	gettimeofday(&tv, NULL);
 | 
			
		||||
	while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
 | 
			
		||||
		res = ast_waitfor(chan, -1);
 | 
			
		||||
		if (res < 0) {
 | 
			
		||||
			ast_closestream(fs);
 | 
			
		||||
			fdprintf(fd, "200 result=%d (waitfor)\n", res);
 | 
			
		||||
			return RESULT_FAILURE;
 | 
			
		||||
		}
 | 
			
		||||
		f = ast_read(chan);
 | 
			
		||||
		if (!f) {
 | 
			
		||||
			fdprintf(fd, "200 result=%d (hangup)\n", 0);
 | 
			
		||||
			ast_closestream(fs);
 | 
			
		||||
			return RESULT_FAILURE;
 | 
			
		||||
		}
 | 
			
		||||
		switch(f->frametype) {
 | 
			
		||||
		case AST_FRAME_DTMF:
 | 
			
		||||
			if (strchr(argv[4], f->subclass)) {
 | 
			
		||||
				/* This is an interrupting chracter */
 | 
			
		||||
				fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
 | 
			
		||||
				ast_closestream(fs);
 | 
			
		||||
				ast_frfree(f);
 | 
			
		||||
				return RESULT_SUCCESS;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case AST_FRAME_VOICE:
 | 
			
		||||
			ast_writestream(fs, f);
 | 
			
		||||
			break;
 | 
			
		||||
		};
 | 
			
		||||
		ast_frfree(f);
 | 
			
		||||
		gettimeofday(&tv, NULL);
 | 
			
		||||
	}
 | 
			
		||||
	fdprintf(fd, "200 result=%d (timeout)\n", 0);
 | 
			
		||||
	ast_closestream(fs);
 | 
			
		||||
	return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char usage_waitfordigit[] = 
 | 
			
		||||
" Usage: WAIT FOR DIGIT <timeout>\n"
 | 
			
		||||
"        Waits up to 'timeout' seconds for channel to receive a DTMF digit.\n"
 | 
			
		||||
" Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
 | 
			
		||||
" the numerical value of the ascii of the digit if one is received.  Use -1\n"
 | 
			
		||||
" for the timeout value if you desire the call to block indefinitely.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_sendtext[] =
 | 
			
		||||
" Usage: SEND TEXT \"<text to send>\"\n"
 | 
			
		||||
"        Sends the given text on a channel.  Most channels do not support the\n"
 | 
			
		||||
" transmission of text.  Returns 0 if text is sent, or if the channel does not\n"
 | 
			
		||||
" support text transmission.  Returns -1 only on error/hangup.  Text\n"
 | 
			
		||||
" consisting of greater than one word should be placed in quotes since the\n"
 | 
			
		||||
" command only accepts a single argument.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_sendimage[] =
 | 
			
		||||
" Usage: SEND IMAGE <image>\n"
 | 
			
		||||
"        Sends the given image on a channel.  Most channels do not support the\n"
 | 
			
		||||
" transmission of images.  Returns 0 if image is sent, or if the channel does not\n"
 | 
			
		||||
" support image transmission.  Returns -1 only on error/hangup.  Image names\n"
 | 
			
		||||
" should not include extensions.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_streamfile[] =
 | 
			
		||||
" Usage: STREAM FILE <filename> <escape digits>\n"
 | 
			
		||||
"        Send the given file, allowing playback to be interrupted by the given\n"
 | 
			
		||||
" digits, if any.  Use double quotes for the digits if you wish none to be\n"
 | 
			
		||||
" permitted.  Returns 0 if playback completes without a digit being pressed, or\n"
 | 
			
		||||
" the ASCII numerical value of the digit if one was pressed, or -1 on error or\n"
 | 
			
		||||
" if the channel was disconnected.  Remember, the file extension must not be\n"
 | 
			
		||||
" included in the filename.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_saynumber[] =
 | 
			
		||||
" Usage: SAY NUMBER <number> <escape digits>\n"
 | 
			
		||||
"        Say a given number, returning early if any of the given DTMF digits\n"
 | 
			
		||||
" are received on the channel.  Returns 0 if playback completes without a digit\n"
 | 
			
		||||
" being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
 | 
			
		||||
" -1 on error/hangup.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_getdata[] =
 | 
			
		||||
" Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
 | 
			
		||||
"	 Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
 | 
			
		||||
"from the channel at the other end.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_setcontext[] =
 | 
			
		||||
" Usage: SET CONTEXT <desired context>\n"
 | 
			
		||||
"	 Sets the context for continuation upon exiting the application.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_setextension[] =
 | 
			
		||||
" Usage: SET EXTENSION <new extension>\n"
 | 
			
		||||
"	 Changes the extension for continuation upon exiting the application.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_setpriority[] =
 | 
			
		||||
" Usage: SET PRIORITY <num>\n"
 | 
			
		||||
"	 Changes the priority for continuation upon exiting the application.\n";
 | 
			
		||||
 | 
			
		||||
static char usage_recordfile[] =
 | 
			
		||||
" Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [BEEP]\n"
 | 
			
		||||
"        Record to a file until a given dtmf digit in the sequence is received\n"
 | 
			
		||||
" Returns -1 on hangup or error.  The format will specify what kind of file\n"
 | 
			
		||||
" will be recorded.  The timeout is the maximum record time in milliseconds, or\n"
 | 
			
		||||
" -1 for no timeout\n";
 | 
			
		||||
 | 
			
		||||
agi_command commands[] = {
 | 
			
		||||
	{ { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
 | 
			
		||||
	{ { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
 | 
			
		||||
	{ { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
 | 
			
		||||
	{ { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
 | 
			
		||||
	{ { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
 | 
			
		||||
	{ { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
 | 
			
		||||
	{ { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
 | 
			
		||||
	{ { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
 | 
			
		||||
	{ { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
 | 
			
		||||
	{ { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static agi_command *find_command(char *cmds[])
 | 
			
		||||
{
 | 
			
		||||
	int x;
 | 
			
		||||
	int y;
 | 
			
		||||
	int match;
 | 
			
		||||
	for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
 | 
			
		||||
		/* start optimistic */
 | 
			
		||||
		match = 1;
 | 
			
		||||
		for (y=0;match && cmds[y]; y++) {
 | 
			
		||||
			/* If there are no more words in the command (and we're looking for
 | 
			
		||||
			   an exact match) or there is a difference between the two words,
 | 
			
		||||
			   then this is not a match */
 | 
			
		||||
			if (!commands[x].cmda[y])
 | 
			
		||||
				break;
 | 
			
		||||
			if (strcasecmp(commands[x].cmda[y], cmds[y]))
 | 
			
		||||
				match = 0;
 | 
			
		||||
		}
 | 
			
		||||
		/* If more words are needed to complete the command then this is not
 | 
			
		||||
		   a candidate (unless we're looking for a really inexact answer  */
 | 
			
		||||
		if (commands[x].cmda[y])
 | 
			
		||||
			match = 0;
 | 
			
		||||
		if (match)
 | 
			
		||||
			return &commands[x];
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int parse_args(char *s, int *max, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	int x=0;
 | 
			
		||||
	int quoted=0;
 | 
			
		||||
	int escaped=0;
 | 
			
		||||
	int whitespace=1;
 | 
			
		||||
	char *cur;
 | 
			
		||||
 | 
			
		||||
	cur = s;
 | 
			
		||||
	while(*s) {
 | 
			
		||||
		switch(*s) {
 | 
			
		||||
		case '"':
 | 
			
		||||
			/* If it's escaped, put a literal quote */
 | 
			
		||||
			if (escaped) 
 | 
			
		||||
				goto normal;
 | 
			
		||||
			else 
 | 
			
		||||
				quoted = !quoted;
 | 
			
		||||
			if (quoted && whitespace) {
 | 
			
		||||
				/* If we're starting a quote, coming off white space start a new word, too */
 | 
			
		||||
				argv[x++] = cur;
 | 
			
		||||
				whitespace=0;
 | 
			
		||||
			}
 | 
			
		||||
			escaped = 0;
 | 
			
		||||
		break;
 | 
			
		||||
		case ' ':
 | 
			
		||||
		case '\t':
 | 
			
		||||
			if (!quoted && !escaped) {
 | 
			
		||||
				/* If we're not quoted, mark this as whitespace, and
 | 
			
		||||
				   end the previous argument */
 | 
			
		||||
				whitespace = 1;
 | 
			
		||||
				*(cur++) = '\0';
 | 
			
		||||
			} else
 | 
			
		||||
				/* Otherwise, just treat it as anything else */ 
 | 
			
		||||
				goto normal;
 | 
			
		||||
			break;
 | 
			
		||||
		case '\\':
 | 
			
		||||
			/* If we're escaped, print a literal, otherwise enable escaping */
 | 
			
		||||
			if (escaped) {
 | 
			
		||||
				goto normal;
 | 
			
		||||
			} else {
 | 
			
		||||
				escaped=1;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
normal:
 | 
			
		||||
			if (whitespace) {
 | 
			
		||||
				if (x >= MAX_ARGS -1) {
 | 
			
		||||
					ast_log(LOG_WARNING, "Too many arguments, truncating\n");
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				/* Coming off of whitespace, start the next argument */
 | 
			
		||||
				argv[x++] = cur;
 | 
			
		||||
				whitespace=0;
 | 
			
		||||
			}
 | 
			
		||||
			*(cur++) = *s;
 | 
			
		||||
			escaped=0;
 | 
			
		||||
		}
 | 
			
		||||
		s++;
 | 
			
		||||
	}
 | 
			
		||||
	/* Null terminate */
 | 
			
		||||
	*(cur++) = '\0';
 | 
			
		||||
	argv[x] = NULL;
 | 
			
		||||
	*max = x;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int agi_handle_command(struct ast_channel *chan, int fd, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	char *argv[MAX_ARGS];
 | 
			
		||||
	int argc = 0;
 | 
			
		||||
	int res;
 | 
			
		||||
	agi_command *c;
 | 
			
		||||
	argc = MAX_ARGS;
 | 
			
		||||
	parse_args(buf, &argc, argv);
 | 
			
		||||
#if	0
 | 
			
		||||
	{ int x;
 | 
			
		||||
	for (x=0;x<argc;x++) 
 | 
			
		||||
		fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
 | 
			
		||||
#endif
 | 
			
		||||
	c = find_command(argv);
 | 
			
		||||
	if (c) {
 | 
			
		||||
		res = c->handler(chan, fd, argc, argv);
 | 
			
		||||
		switch(res) {
 | 
			
		||||
		case RESULT_SHOWUSAGE:
 | 
			
		||||
			fdprintf(fd, "520-Invalid command syntax.  Proper usage follows:\n");
 | 
			
		||||
			fdprintf(fd, c->usage);
 | 
			
		||||
			fdprintf(fd, "520 End of proper usage.\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case RESULT_FAILURE:
 | 
			
		||||
			/* They've already given the failure.  We've been hung up on so handle this
 | 
			
		||||
			   appropriately */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		fdprintf(fd, "510 Invalid or unknown command\n");
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid)
 | 
			
		||||
{
 | 
			
		||||
	struct ast_channel *c;
 | 
			
		||||
	int outfd;
 | 
			
		||||
	int ms;
 | 
			
		||||
	int returnstatus = 0;
 | 
			
		||||
	struct ast_frame *f;
 | 
			
		||||
	char buf[2048];
 | 
			
		||||
	FILE *readf;
 | 
			
		||||
	if (!(readf = fdopen(fds[0], "r"))) {
 | 
			
		||||
		ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
 | 
			
		||||
		kill(pid, SIGHUP);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	setlinebuf(readf);
 | 
			
		||||
	setup_env(chan, request, fds[1]);
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		ms = -1;
 | 
			
		||||
		c = ast_waitfor_nandfds(&chan, 1, &fds[0], 1, NULL, &outfd, &ms);
 | 
			
		||||
		if (c) {
 | 
			
		||||
			/* Idle the channel until we get a command */
 | 
			
		||||
			f = ast_read(c);
 | 
			
		||||
			if (!f) {
 | 
			
		||||
				ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
 | 
			
		||||
				returnstatus = -1;
 | 
			
		||||
				break;
 | 
			
		||||
			} else {
 | 
			
		||||
				ast_frfree(f);
 | 
			
		||||
			}
 | 
			
		||||
		} else if (outfd > -1) {
 | 
			
		||||
			if (!fgets(buf, sizeof(buf), readf)) {
 | 
			
		||||
				/* Program terminated */
 | 
			
		||||
				if (returnstatus)
 | 
			
		||||
					returnstatus = -1;
 | 
			
		||||
				if (option_verbose > 2) 
 | 
			
		||||
					ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
 | 
			
		||||
				/* No need to kill the pid anymore, since they closed us */
 | 
			
		||||
				pid = -1;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			returnstatus |= agi_handle_command(chan, fds[1], buf);
 | 
			
		||||
			/* If the handle_command returns -1, we need to stop */
 | 
			
		||||
			if (returnstatus < 0) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			ast_log(LOG_WARNING, "No channel, no fd?\n");
 | 
			
		||||
			returnstatus = -1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* Notify process */
 | 
			
		||||
	if (pid > -1)
 | 
			
		||||
		kill(pid, SIGHUP);
 | 
			
		||||
	fclose(readf);
 | 
			
		||||
	return returnstatus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int agi_exec(struct ast_channel *chan, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int res=0;
 | 
			
		||||
	struct localuser *u;
 | 
			
		||||
	char *args,*ringy;
 | 
			
		||||
	char tmp[256];
 | 
			
		||||
	int fds[2];
 | 
			
		||||
	int pid;
 | 
			
		||||
	if (!data || !strlen(data)) {
 | 
			
		||||
		ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	strncpy(tmp, data, sizeof(tmp));
 | 
			
		||||
	strtok(tmp, "|");
 | 
			
		||||
	args = strtok(NULL, "|");
 | 
			
		||||
	ringy = strtok(NULL,"|");
 | 
			
		||||
	if (!args)
 | 
			
		||||
		args = "";
 | 
			
		||||
	LOCAL_USER_ADD(u);
 | 
			
		||||
	 /* Answer if need be */
 | 
			
		||||
        if (chan->state != AST_STATE_UP) {
 | 
			
		||||
		if (ringy) { /* if for ringing first */
 | 
			
		||||
			/* a little ringy-dingy first */
 | 
			
		||||
		        ast_indicate(chan, AST_CONTROL_RINGING);  
 | 
			
		||||
			sleep(3); 
 | 
			
		||||
		}
 | 
			
		||||
		if (ast_answer(chan)) {
 | 
			
		||||
			LOCAL_USER_REMOVE(u);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	res = launch_script(tmp, args, fds, &pid);
 | 
			
		||||
	if (!res) {
 | 
			
		||||
		res = run_agi(chan, tmp, fds, pid);
 | 
			
		||||
		close(fds[0]);
 | 
			
		||||
		close(fds[1]);
 | 
			
		||||
	}
 | 
			
		||||
	LOCAL_USER_REMOVE(u);
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int unload_module(void)
 | 
			
		||||
{
 | 
			
		||||
	STANDARD_HANGUP_LOCALUSERS;
 | 
			
		||||
	return ast_unregister_application(app);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int load_module(void)
 | 
			
		||||
{
 | 
			
		||||
	return ast_register_application(app, agi_exec, synopsis, descrip);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *description(void)
 | 
			
		||||
{
 | 
			
		||||
	return tdesc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int usecount(void)
 | 
			
		||||
{
 | 
			
		||||
	int res;
 | 
			
		||||
	STANDARD_USECOUNT(res);
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *key()
 | 
			
		||||
{
 | 
			
		||||
	return ASTERISK_GPL_KEY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CLIP 32635
 | 
			
		||||
#define BIAS 0x84
 | 
			
		||||
 | 
			
		||||
unsigned char
 | 
			
		||||
linear2ulaw(sample)
 | 
			
		||||
short sample; {
 | 
			
		||||
  static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
 | 
			
		||||
                             4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
 | 
			
		||||
                             5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
 | 
			
		||||
                             5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
 | 
			
		||||
                             6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 | 
			
		||||
                             6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 | 
			
		||||
                             6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 | 
			
		||||
                             6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 | 
			
		||||
                             7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
 | 
			
		||||
  int sign, exponent, mantissa;
 | 
			
		||||
  unsigned char ulawbyte;
 | 
			
		||||
 
 | 
			
		||||
  /* Get the sample into sign-magnitude. */
 | 
			
		||||
  sign = (sample >> 8) & 0x80;          /* set aside the sign */
 | 
			
		||||
  if (sign != 0) sample = -sample;              /* get magnitude */
 | 
			
		||||
  if (sample > CLIP) sample = CLIP;             /* clip the magnitude */
 | 
			
		||||
 
 | 
			
		||||
  /* Convert from 16 bit linear to ulaw. */
 | 
			
		||||
  sample = sample + BIAS;
 | 
			
		||||
  exponent = exp_lut[(sample >> 7) & 0xFF];
 | 
			
		||||
  mantissa = (sample >> (exponent + 3)) & 0x0F;
 | 
			
		||||
  ulawbyte = ~(sign | (exponent << 4) | mantissa);
 | 
			
		||||
#ifdef ZEROTRAP
 | 
			
		||||
  if (ulawbyte == 0) ulawbyte = 0x02;   /* optional CCITT trap */
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
  return(ulawbyte);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user