Fix typo's (retrieve, specified, address).
[asterisk/asterisk.git] / contrib / utils / eagi_proxy.c
1 /*
2  * Asterisk EAGI -> TCP/IP proxy
3  *      by Danijel Korzinek (devil_slayer _at_ hotmail.com)
4  *
5  * This simple C application allows you to control asterisk thru one TCP/IP
6  * socket and listen to the conversation thru another socket.
7  *
8  * Great for ASR or wizzard-of-oz telephony systems!
9  *
10  * HOWTO:
11  *      The program is compiled using the following command:
12  *              gcc eagi_proxy.c -o eagi_proxy -lpthread
13  *
14  *      In the dialplan, you can add something like this to the main context:
15  *              exten => s,1,Answer
16  *              exten => s,n,EAGI(/path/to/eagi_proxy)
17  *              exten => s,n,Hangup
18  *
19  *      To test the program you can use the netcat utility:
20  *              (http://netcat.sourceforge.net/)
21  *
22  *              -in one console run:
23  *                      nc -vv -l -p 8418 > /path/to/file.raw
24  *              -in another run:
25  *                      nc -vv -l -p 8417
26  *              -you can use any file for the signal or even /dev/null
27  *              -you can change the port numbers in the sourcecode below
28  *
29  *      Once you make the call, both programs will accept the incoming
30  *      connection. The program on port 8417 will print out the enviornemnt
31  *      (unless the appropriate define below is commented) and you can write
32  *      any AGI command there (http://www.voip-info.org/wiki-Asterisk+AGI),
33  *      e.g.:
34  *              GET DATA /path/to/gsm/file 10000 4
35  *
36  *      Finally, you can open the RAW file in any sound editor. The format is:
37  *              RAW little-endian 8kHz 16bit
38  */
39
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <string.h>
46 #include <time.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/socket.h>
50 #include <sys/time.h>
51 #include <arpa/inet.h>
52 #include <netdb.h>
53 #include <fcntl.h>
54 #include <errno.h>
55 #include <ctype.h>
56 #include <pthread.h>
57
58 /* DEFINES */
59 #define SIGNAL_PORT 8418
60 #define COMMAND_PORT 8417
61 #define SEND_ENVIORNMENT /*send the enviornment thru the socket*/
62 /************************/
63
64
65 #define BUFSIZE 1024
66 char buf[BUFSIZE];
67
68 #define WINSIZE 400 /* 25 ms @ 8 kHz and 16bit */
69 char window[WINSIZE];
70
71 #define WINBUF_NUM 2400 /* number of WINSIZE windows = 1 minute */
72 char* winbuf;
73 char *end, *bs, *be;
74 /* winbuf - start of buffer 
75  * end - end of buffer  
76  * bs - start of data
77  * be - end of data 
78  */
79
80 int command_desc; /* command transfer descriptor */
81 int speech_desc; /* speech signal descrriptor */
82 char connected=1; /* connection state */
83
84 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*/
85
86 void read_full(int file, char* buffer, int num); /* read EXACTLY "num" ammount of bytes from "file" descriptor to "buffer"*/
87 int read_some(int file, char* buffer, int size); /* read AT MOST "size" ammount of bytes */
88
89 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 */
90 int write_amap(int file, char* buffer, int num); /*write AT MOST "num" ammount of bytes and return ammount that was written*/
91
92 void setnonblocking(int desc); /*sets the socket non-blocking; for polling */
93
94 void finalize(); /* this function is run at exit */
95
96 pthread_mutex_t command_mutex;/* command socket mutex */
97 pthread_t stdin_thread,signal_thread;
98 void* readStdin(void* ptr);
99 void* readSignal(void* ptr);
100
101 /* The program creates 3 threads:
102  * 1) Main thread - reads commands from the socket and sends them to asterisk
103  * 2) stdin_thread - reads asterisk output and sends it to the command socket
104  * 3) signal_thread - reads the sound from asterisk and sends it to the signal socket
105  */
106
107 int main()
108 {
109         int ret;
110         
111         atexit(finalize);
112
113         setlinebuf(stdin);
114         setlinebuf(stdout);
115
116         winbuf=(char*)malloc(WINSIZE*WINBUF_NUM);
117         end=winbuf+WINSIZE*WINBUF_NUM;
118         bs=be=winbuf;
119
120         speech_desc=connect_to_host("localhost",SIGNAL_PORT);
121         if(speech_desc<0) 
122         {
123                 perror("signal socket");
124                 return -1;
125         }
126
127
128         command_desc=connect_to_host("localhost",COMMAND_PORT);
129         if(command_desc<0) 
130         {
131                 perror("command socket");
132                 return -1;
133         }
134
135         pthread_mutex_init(&command_mutex,NULL);
136         pthread_create(&stdin_thread,NULL,readStdin,NULL);
137         pthread_create(&signal_thread,NULL,readSignal,NULL);
138
139         while(connected)
140         {       
141                 pthread_mutex_lock(&command_mutex);
142                 ret=read_some(command_desc,buf,BUFSIZE);
143                 pthread_mutex_unlock(&command_mutex);
144                 if(ret>0)
145                 {
146                         buf[ret]=0;
147                         printf("%s",buf);                       
148                 }
149         }
150
151         return 0;
152 }
153
154 void finalize()
155 {
156         close(command_desc);
157         close(speech_desc);
158         free(winbuf);   
159 }
160
161 void* readStdin(void* ptr)
162 {
163         while(1)/*read enviornment*/
164         {
165                 fgets(buf,BUFSIZE,stdin);
166                 #ifdef SEND_ENVIORNMENT
167                         pthread_mutex_lock(&command_mutex);
168                         write_buf(command_desc,buf,strlen(buf));
169                         pthread_mutex_unlock(&command_mutex);
170                 #endif
171                 if(feof(stdin) || buf[0]=='\n') 
172                 {
173                         break;
174                 }
175         }
176
177         while(connected)
178         {
179                 fgets(buf,BUFSIZE,stdin);
180                 pthread_mutex_lock(&command_mutex);
181                 write_buf(command_desc,buf,strlen(buf));
182                 pthread_mutex_unlock(&command_mutex);
183         }
184
185         pthread_exit(NULL);
186 }
187
188 void* readSignal(void* ptr)
189 {
190         while(connected)
191         {
192                 read_full(3,window,WINSIZE);
193                 write_buf(speech_desc,window,WINSIZE);
194         }
195
196         pthread_exit(NULL);
197 }
198
199
200 void read_full(int file, char* buffer, int num)
201 {
202         int count,pos=0;
203
204         while(num)
205         {
206                 count=read(file,buffer+pos,num);
207                 if(count==0 || (count<0 && errno!=EAGAIN))
208                 {
209                         connected=0;
210                         return;
211                 }
212                 num-=count;
213                 pos+=count;
214         }
215 }
216
217 int connect_to_host(char* name, int port)
218 {
219         int address;
220         struct hostent* host_entity;
221         int res,desc;
222         int opts;
223         struct sockaddr_in host;
224         
225
226         /* get address */       
227         if(!strcmp(name,"localhost"))
228                 address=htonl(2130706433); /*127.0.0.1*/
229         else
230         {
231                 address=inet_addr(name); /* check if it's an IP that's written in the string */
232                 if(address==(in_addr_t)-1)
233                 {
234                         host_entity = gethostbyname(name); /* search for the host under this name */
235         
236                         if(!host_entity)
237                         {
238                                 fprintf(stderr,"EAGI proxy: Wrong address!\n"); /* can't find anything*/
239                                 return -1;
240                         }
241                         address=*((int*)host_entity->h_addr);
242                 }
243         }
244
245         desc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
246         if(desc<0)
247         {
248                 fprintf(stderr,"EAGI proxy: Cannot create socket!\n");
249                 return -1;
250         }
251
252         memset((void*)&host,0,sizeof(struct sockaddr_in));
253         
254         host.sin_family=AF_INET;
255         host.sin_port=htons(port);
256         host.sin_addr.s_addr=address;
257         
258         res=connect(desc,(struct sockaddr*)&host,sizeof(host));
259         if(res<0)
260         {
261                 fprintf(stderr,"EAGI proxy: Cannot connect!\n");
262                 return -1;
263         }
264
265         /* set to non-blocking mode */
266         opts = fcntl(desc,F_GETFL);
267         if (opts < 0) {
268                 perror("fcntl(F_GETFL)");
269                 exit(EXIT_FAILURE);
270         }
271         opts = (opts | O_NONBLOCK);
272         if (fcntl(desc,F_SETFL,opts) < 0) {
273                 perror("fcntl(F_SETFL)");
274                 exit(EXIT_FAILURE);
275         }
276
277
278         return desc;
279 }
280
281 int read_some(int desc, char* buffer, int size)
282 {
283         unsigned char c;
284         int res,i=0;
285
286         for(;;)
287         {
288                 res=read(desc,&c,1);
289                 if(res<1)
290                 {
291                         if(errno!=EAGAIN)
292                         {
293                                 perror("Error reading");
294                                 connected=0;
295                         }       
296                         break;
297                 }
298                 if(res==0) 
299                 {
300                         connected=0;
301                         break;
302                 }
303                 
304                 buffer[i]=c;
305                 i++;
306         }
307
308         return i;
309 }
310
311 /* This is a tricky function! */
312 void write_buf(int desc, char* buf, int size)
313 {
314         int ret;
315
316         /*NOTE: AMAP -> as much as possible */
317
318         if(be!=bs)/* if data left in buffer */
319         {
320                 if(be>bs)/* if buffer not split */
321                 {
322                         ret=write_amap(desc,bs,be-bs);/* write AMAP */
323                         bs+=ret;/* shift the start of the buffer */
324                 }
325                 else/* if buffer is split */
326                 {
327                         ret=write_amap(desc,bs,end-bs);/* write higher part first */
328                         if(ret==end-bs)/* if wrote whole of the higher part */
329                         {
330                                 ret=write_amap(desc,winbuf,be-winbuf);/* write lower part */
331                                 bs=winbuf+ret;/* shift start to new position */
332                         }
333                         else bs+=ret;/* if not wrote whole of higher part, only shift start */
334                 }
335         }
336
337         if(be==bs)/* if buffer is empty now */
338         {
339                 ret=write_amap(desc,buf,size);/* write AMAP of the new data */
340                 buf+=ret;/* shift start of new data */
341                 size-=ret;/* lower size of new data */
342         }
343
344         if(size)/* if new data still remains unsent */
345         {
346                 if(be>=bs)/* if data not split */
347                 {
348                         if(size>end-be)/* if new data size doesn't fit higher end */
349                         {
350                                 size-=end-be;/* reduce new data size by the higher end size */
351                                 memcpy(be,buf,end-be);/* copy to higher end */
352                                 be=winbuf;/* shift end to begining of buffer */
353                                 buf+=end-be;/* shift start of new data */
354                         }
355                         else/* if new data fits the higher end */
356                         {
357                                 memcpy(be,buf,size);/* copy to higher end */
358                                 be+=size;/* shift end by size */
359                                 if(be>=end)/* if end goes beyond the buffer */
360                                         be=winbuf;/* restart */
361                                 size=0;/* everything copied */
362                         }
363                 }
364
365                 if(size)/* if new data still remains */
366                 {
367                         if(size>=bs-be)/* if new data doesn't fit between end and start */
368                         {
369                                 fprintf(stderr,"Buffer overflow!\n");
370                                 size=bs-be-1;/* reduce the size that we can copy */
371                         }
372
373                         if(size)/* if we can copy anything */
374                         {
375                                 memcpy(be,buf,size);/* copy the new data between end and start */
376                                 be+=size;/* shift end by size */
377                         }
378                 }
379         }
380 }
381
382 int write_amap(int desc, char* buf, int size)
383 {
384         int ret;
385         ret=write(desc,buf,size);
386         if(ret<0)
387         {
388                 if(errno!=EAGAIN) 
389                 {
390                         perror("Error writing");
391                         connected=0;
392                 }
393                 return 0;
394         }
395         if(ret==0)
396                 connected=0;
397
398         return ret;
399 }
400
401
402 void setnonblocking(int desc)
403 {
404         int opts;
405         
406         opts = fcntl(desc,F_GETFL);
407         if(opts < 0)
408         {
409                 perror("fcntl(F_GETFL)");
410                 exit(-1);
411         }
412
413         opts = (opts | O_NONBLOCK );
414         if(fcntl(desc,F_SETFL,opts) < 0)
415         {
416                 perror("fcntl(F_SETFL)");
417                 exit(-1);
418         }
419 }