aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/utils/eagi_proxy.c
blob: 03c7e0640613bfdea551f22adb9081b04f21fb11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
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 <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <pthread.h>

/* 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);
	}
}