From d2c459bc0e147ff9085d27084204bcac8449a70e Mon Sep 17 00:00:00 2001 From: tilghman Date: Wed, 7 Nov 2007 01:08:31 +0000 Subject: Add contributed EAGI proxy, which provides FastAGI functionality for EAGI, while also buffering the audio stream. Reported by: devil_slayer Patch by: devil_slayer Closes issue #8921 git-svn-id: http://svn.digium.com/svn/asterisk/trunk@89077 f38db490-d61c-443f-a65b-d21fe96a405b --- contrib/utils/eagi_proxy.c | 419 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 contrib/utils/eagi_proxy.c (limited to 'contrib/utils') diff --git a/contrib/utils/eagi_proxy.c b/contrib/utils/eagi_proxy.c new file mode 100644 index 000000000..03c7e0640 --- /dev/null +++ b/contrib/utils/eagi_proxy.c @@ -0,0 +1,419 @@ +/* + * Asterisk EAGI -> TCP/IP proxy + * by Danijel Korzinek (devil_slayer _at_ hotmail.com) + * + * This simple C application allows you to control asterisk thru one TCP/IP + * socket and listen to the conversation thru another socket. + * + * Great for ASR or wizzard-of-oz telephony systems! + * + * HOWTO: + * The program is compiled using the following command: + * gcc eagi_proxy.c -o eagi_proxy -lpthread + * + * In the dialplan, you can add something like this to the main context: + * exten => s,1,Answer + * exten => s,n,EAGI(/path/to/eagi_proxy) + * exten => s,n,Hangup + * + * To test the program you can use the netcat utility: + * (http://netcat.sourceforge.net/) + * + * -in one console run: + * nc -vv -l -p 8418 > /path/to/file.raw + * -in another run: + * nc -vv -l -p 8417 + * -you can use any file for the signal or even /dev/null + * -you can change the port numbers in the sourcecode below + * + * Once you make the call, both programs will accept the incoming + * connection. The program on port 8417 will print out the enviornemnt + * (unless the appropriate define below is commented) and you can write + * any AGI command there (http://www.voip-info.org/wiki-Asterisk+AGI), + * e.g.: + * GET DATA /path/to/gsm/file 10000 4 + * + * Finally, you can open the RAW file in any sound editor. The format is: + * RAW little-endian 8kHz 16bit + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DEFINES */ +#define SIGNAL_PORT 8418 +#define COMMAND_PORT 8417 +#define SEND_ENVIORNMENT /*send the enviornment thru the socket*/ +/************************/ + + +#define BUFSIZE 1024 +char buf[BUFSIZE]; + +#define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */ +char window[WINSIZE]; + +#define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */ +char* winbuf; +char *end, *bs, *be; +/* winbuf - start of buffer + * end - end of buffer + * bs - start of data + * be - end of data + */ + +int command_desc; /* command transfer descriptor */ +int speech_desc; /* speech signal descrriptor */ +char connected=1; /* connection state */ + +int connect_to_host(char* host, int port); /* connect to a given host (name or IP) and given port number in nonblocking mode returning socket descriptor*/ + +void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/ +int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */ + +void write_buf(int file, char* buffer, int num); /* write "num" ammount of bytes to "file" descriptor and buffer the surplus if the write would block */ +int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/ + +void setnonblocking(int desc); /*sets the socket non-blocking; for polling */ + +void finalize(); /* this function is run at exit */ + +pthread_mutex_t command_mutex;/* command socket mutex */ +pthread_t stdin_thread,signal_thread; +void* readStdin(void* ptr); +void* readSignal(void* ptr); + +/* The program creates 3 threads: + * 1) Main thread - reads commands from the socket and sends them to asterisk + * 2) stdin_thread - reads asterisk output and sends it to the command socket + * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket + */ + +int main() +{ + int ret; + + atexit(finalize); + + setlinebuf(stdin); + setlinebuf(stdout); + + winbuf=(char*)malloc(WINSIZE*WINBUF_NUM); + end=winbuf+WINSIZE*WINBUF_NUM; + bs=be=winbuf; + + speech_desc=connect_to_host("localhost",SIGNAL_PORT); + if(speech_desc<0) + { + perror("signal socket"); + return -1; + } + + + command_desc=connect_to_host("localhost",COMMAND_PORT); + if(command_desc<0) + { + perror("command socket"); + return -1; + } + + pthread_mutex_init(&command_mutex,NULL); + pthread_create(&stdin_thread,NULL,readStdin,NULL); + pthread_create(&signal_thread,NULL,readSignal,NULL); + + while(connected) + { + pthread_mutex_lock(&command_mutex); + ret=read_some(command_desc,buf,BUFSIZE); + pthread_mutex_unlock(&command_mutex); + if(ret>0) + { + buf[ret]=0; + printf("%s",buf); + } + } + + return 0; +} + +void finalize() +{ + close(command_desc); + close(speech_desc); + free(winbuf); +} + +void* readStdin(void* ptr) +{ + while(1)/*read enviornment*/ + { + fgets(buf,BUFSIZE,stdin); + #ifdef SEND_ENVIORNMENT + pthread_mutex_lock(&command_mutex); + write_buf(command_desc,buf,strlen(buf)); + pthread_mutex_unlock(&command_mutex); + #endif + if(feof(stdin) || buf[0]=='\n') + { + break; + } + } + + while(connected) + { + fgets(buf,BUFSIZE,stdin); + pthread_mutex_lock(&command_mutex); + write_buf(command_desc,buf,strlen(buf)); + pthread_mutex_unlock(&command_mutex); + } + + pthread_exit(NULL); +} + +void* readSignal(void* ptr) +{ + while(connected) + { + read_full(3,window,WINSIZE); + write_buf(speech_desc,window,WINSIZE); + } + + pthread_exit(NULL); +} + + +void read_full(int file, char* buffer, int num) +{ + int count,pos=0; + + while(num) + { + count=read(file,buffer+pos,num); + if(count==0 || (count<0 && errno!=EAGAIN)) + { + connected=0; + return; + } + num-=count; + pos+=count; + } +} + +int connect_to_host(char* name, int port) +{ + int address; + struct hostent* host_entity; + int res,desc; + int opts; + struct sockaddr_in host; + + + /* get adress */ + if(!strcmp(name,"localhost")) + address=htonl(2130706433); /*127.0.0.1*/ + else + { + address=inet_addr(name); /* check if it's an IP that's written in the string */ + if(address==(in_addr_t)-1) + { + host_entity = gethostbyname(name); /* search for the host under this name */ + + if(!host_entity) + { + fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/ + return -1; + } + address=*((int*)host_entity->h_addr); + } + } + + desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if(desc<0) + { + fprintf(stderr,"EAGI proxy: Cannot create socket!\n"); + return -1; + } + + memset((void*)&host,0,sizeof(struct sockaddr_in)); + + host.sin_family=AF_INET; + host.sin_port=htons(port); + host.sin_addr.s_addr=address; + + res=connect(desc,(struct sockaddr*)&host,sizeof(host)); + if(res<0) + { + fprintf(stderr,"EAGI proxy: Cannot connect!\n"); + return -1; + } + + /* set to non-blocking mode */ + opts = fcntl(desc,F_GETFL); + if (opts < 0) { + perror("fcntl(F_GETFL)"); + exit(EXIT_FAILURE); + } + opts = (opts | O_NONBLOCK); + if (fcntl(desc,F_SETFL,opts) < 0) { + perror("fcntl(F_SETFL)"); + exit(EXIT_FAILURE); + } + + + return desc; +} + +int read_some(int desc, char* buffer, int size) +{ + unsigned char c; + int res,i=0; + + for(;;) + { + res=read(desc,&c,1); + if(res<1) + { + if(errno!=EAGAIN) + { + perror("Error reading"); + connected=0; + } + break; + } + if(res==0) + { + connected=0; + break; + } + + buffer[i]=c; + i++; + } + + return i; +} + +/* This is a tricky function! */ +void write_buf(int desc, char* buf, int size) +{ + int ret; + + /*NOTE: AMAP -> as much as possible */ + + if(be!=bs)/* if data left in buffer */ + { + if(be>bs)/* if buffer not split */ + { + ret=write_amap(desc,bs,be-bs);/* write AMAP */ + bs+=ret;/* shift the start of the buffer */ + } + else/* if buffer is split */ + { + ret=write_amap(desc,bs,end-bs);/* write higher part first */ + if(ret==end-bs)/* if wrote whole of the higher part */ + { + ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */ + bs=winbuf+ret;/* shift start to new position */ + } + else bs+=ret;/* if not wrote whole of higher part, only shift start */ + } + } + + if(be==bs)/* if buffer is empty now */ + { + ret=write_amap(desc,buf,size);/* write AMAP of the new data */ + buf+=ret;/* shift start of new data */ + size-=ret;/* lower size of new data */ + } + + if(size)/* if new data still remains unsent */ + { + if(be>=bs)/* if data not split */ + { + if(size>end-be)/* if new data size doesn't fit higher end */ + { + size-=end-be;/* reduce new data size by the higher end size */ + memcpy(be,buf,end-be);/* copy to higher end */ + be=winbuf;/* shift end to begining of buffer */ + buf+=end-be;/* shift start of new data */ + } + else/* if new data fits the higher end */ + { + memcpy(be,buf,size);/* copy to higher end */ + be+=size;/* shift end by size */ + if(be>=end)/* if end goes beyond the buffer */ + be=winbuf;/* restart */ + size=0;/* everything copied */ + } + } + + if(size)/* if new data still remains */ + { + if(size>=bs-be)/* if new data doesn't fit between end and start */ + { + fprintf(stderr,"Buffer overflow!\n"); + size=bs-be-1;/* reduce the size that we can copy */ + } + + if(size)/* if we can copy anything */ + { + memcpy(be,buf,size);/* copy the new data between end and start */ + be+=size;/* shift end by size */ + } + } + } +} + +int write_amap(int desc, char* buf, int size) +{ + int ret; + ret=write(desc,buf,size); + if(ret<0) + { + if(errno!=EAGAIN) + { + perror("Error writing"); + connected=0; + } + return 0; + } + if(ret==0) + connected=0; + + return ret; +} + + +void setnonblocking(int desc) +{ + int opts; + + opts = fcntl(desc,F_GETFL); + if(opts < 0) + { + perror("fcntl(F_GETFL)"); + exit(-1); + } + + opts = (opts | O_NONBLOCK ); + if(fcntl(desc,F_SETFL,opts) < 0) + { + perror("fcntl(F_SETFL)"); + exit(-1); + } +} -- cgit v1.2.3