Merge rgagnon's pedantic string changes (apps n-z) (bug #2038)
[asterisk/asterisk.git] / apps / app_zapscan.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Zap Scanner
5  *
6  * Copyright (C) 2003, Digium
7  *
8  * Modified from app_zapbarge by David Troy <dave@toad.net>
9  *
10  * Mark Spencer <markster@digium.com>
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License
14  *
15  * Special thanks to comphealth.com for sponsoring this
16  * GPL application.
17  */
18
19 #include <asterisk/lock.h>
20 #include <asterisk/file.h>
21 #include <asterisk/logger.h>
22 #include <asterisk/channel.h>
23 #include <asterisk/pbx.h>
24 #include <asterisk/module.h>
25 #include <asterisk/config.h>
26 #include <asterisk/app.h>
27 #include <asterisk/options.h>
28 #include <asterisk/cli.h>
29 #include <asterisk/say.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <sys/ioctl.h>
36
37 #ifdef __linux__
38 #include <linux/zaptel.h>
39 #else
40 #include <zaptel.h>
41 #endif /* __linux__ */
42
43 static char *tdesc = "Scan Zap channels application";
44
45 static char *app = "ZapScan";
46
47 static char *synopsis = "Scan Zap channels to monitor calls";
48
49 static char *descrip =
50 "  ZapScan allows a call center manager to monitor Zap channels in\n"
51 "a convenient way.  Use '#' to select the next channel and use '*' to exit\n";
52
53
54 STANDARD_LOCAL_USER;
55
56 LOCAL_USER_DECL;
57
58
59 #define CONF_SIZE 160
60
61 static struct ast_channel *get_zap_channel_locked(int num) {
62         struct ast_channel *c=NULL;
63         char name[80];
64
65         snprintf(name,sizeof(name),"Zap/%d-1",num);
66         c = ast_channel_walk_locked(NULL);
67         while(c) {
68                 if (!strcasecmp(c->name, name)) {
69                         break;
70                 }
71                 ast_mutex_unlock(&c->lock);
72                 c = ast_channel_walk_locked(c);
73         }
74         return c;
75 }
76
77 static int careful_write(int fd, unsigned char *data, int len)
78 {
79         int res;
80         while(len) {
81                 res = write(fd, data, len);
82                 if (res < 1) {
83                         if (errno != EAGAIN) {
84                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
85                                 return -1;
86                         } else
87                                 return 0;
88                 }
89                 len -= res;
90                 data += res;
91         }
92         return 0;
93 }
94
95 static int conf_run(struct ast_channel *chan, int confno, int confflags)
96 {
97         int fd;
98         struct zt_confinfo ztc;
99         struct ast_frame *f;
100         struct ast_channel *c;
101         struct ast_frame fr;
102         int outfd;
103         int ms;
104         int nfds;
105         int res;
106         int flags;
107         int retryzap;
108         int origfd;
109         int ret = -1;
110                 char input[4];
111                 int ic=0;
112
113         ZT_BUFFERINFO bi;
114         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
115         char *buf = __buf + AST_FRIENDLY_OFFSET;
116
117         /* Set it into U-law mode (write) */
118         if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
119                 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
120                 goto outrun;
121         }
122
123         /* Set it into U-law mode (read) */
124         if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
125                 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
126                 goto outrun;
127         }
128         ast_indicate(chan, -1);
129         retryzap = strcasecmp(chan->type, "Zap");
130 zapretry:
131         origfd = chan->fds[0];
132         if (retryzap) {
133                 fd = open("/dev/zap/pseudo", O_RDWR);
134                 if (fd < 0) {
135                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
136                         goto outrun;
137                 }
138                 /* Make non-blocking */
139                 flags = fcntl(fd, F_GETFL);
140                 if (flags < 0) {
141                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
142                         close(fd);
143                         goto outrun;
144                 }
145                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
146                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
147                         close(fd);
148                         goto outrun;
149                 }
150                 /* Setup buffering information */
151                 memset(&bi, 0, sizeof(bi));
152                 bi.bufsize = CONF_SIZE;
153                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
154                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
155                 bi.numbufs = 4;
156                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
157                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
158                         close(fd);
159                         goto outrun;
160                 }
161                 nfds = 1;
162         } else {
163                 /* XXX Make sure we're not running on a pseudo channel XXX */
164                 fd = chan->fds[0];
165                 nfds = 0;
166         }
167         memset(&ztc, 0, sizeof(ztc));
168         /* Check to see if we're in a conference... */
169         ztc.chan = 0;
170         if (ioctl(fd, ZT_GETCONF, &ztc)) {
171                 ast_log(LOG_WARNING, "Error getting conference\n");
172                 close(fd);
173                 goto outrun;
174         }
175         if (ztc.confmode) {
176                 /* Whoa, already in a conference...  Retry... */
177                 if (!retryzap) {
178                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
179                         retryzap = 1;
180                         goto zapretry;
181                 }
182         }
183         memset(&ztc, 0, sizeof(ztc));
184         /* Add us to the conference */
185         ztc.chan = 0;
186         ztc.confno = confno;
187         ztc.confmode = ZT_CONF_MONITORBOTH;
188
189         if (ioctl(fd, ZT_SETCONF, &ztc)) {
190                 ast_log(LOG_WARNING, "Error setting conference\n");
191                 close(fd);
192                 goto outrun;
193         }
194         ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
195
196         for(;;) {
197                 outfd = -1;
198                 ms = -1;
199                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
200                 if (c) {
201                         if (c->fds[0] != origfd) {
202                                 if (retryzap) {
203                                         /* Kill old pseudo */
204                                         close(fd);
205                                 }
206                                 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
207                                 retryzap = 0;
208                                 goto zapretry;
209                         }
210                         f = ast_read(c);
211                         if (!f)
212                                 break;
213                                                 if(f->frametype == AST_FRAME_DTMF) {
214                                                         if(f->subclass == '#') {
215                                                                 ret = 0;
216                                 break;
217                                                         }
218                                                         else if (f->subclass == '*') {
219                                                                 ret = -1;
220                                                                 break;
221                                                                 
222                                                         }
223                                                         else {
224                                                                 input[ic++] = f->subclass;
225                                                         }
226                                                         if(ic == 3) {
227                                                                 input[ic++] = '\0';
228                                                                 ic=0;
229                                                                 ret = atoi(input);
230                                                                 ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
231                                                                 break;
232                                                         }
233                                                 }
234
235                                                 if (fd != chan->fds[0]) {
236                                                         if (f->frametype == AST_FRAME_VOICE) {
237                                         if (f->subclass == AST_FORMAT_ULAW) {
238                                                 /* Carefully write */
239                                                 careful_write(fd, f->data, f->datalen);
240                                         } else
241                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
242                                 }
243                         }
244                         ast_frfree(f);
245                 } else if (outfd > -1) {
246                         res = read(outfd, buf, CONF_SIZE);
247                         if (res > 0) {
248                                 memset(&fr, 0, sizeof(fr));
249                                 fr.frametype = AST_FRAME_VOICE;
250                                 fr.subclass = AST_FORMAT_ULAW;
251                                 fr.datalen = res;
252                                 fr.samples = res;
253                                 fr.data = buf;
254                                 fr.offset = AST_FRIENDLY_OFFSET;
255                                 if (ast_write(chan, &fr) < 0) {
256                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
257                                         /* break; */
258                                 }
259                         } else
260                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
261                 }
262         }
263         if (fd != chan->fds[0])
264                 close(fd);
265         else {
266                 /* Take out of conference */
267                 /* Add us to the conference */
268                 ztc.chan = 0;
269                 ztc.confno = 0;
270                 ztc.confmode = 0;
271                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
272                         ast_log(LOG_WARNING, "Error setting conference\n");
273                 }
274         }
275
276 outrun:
277
278         return ret;
279 }
280
281 static int conf_exec(struct ast_channel *chan, void *data)
282 {
283         int res=-1;
284         struct localuser *u;
285         int confflags = 0;
286         int confno = 0;
287         char confstr[80] = "", *tmp;
288         struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
289                 struct ast_frame *f;
290                 int input=0;
291                 
292         LOCAL_USER_ADD(u);
293
294         if (chan->_state != AST_STATE_UP)
295                 ast_answer(chan);
296
297         for (;;) {
298                         if (ast_waitfor(chan, 100) < 0)
299                                 break;
300                         
301                         f = ast_read(chan);
302                         if (!f)
303                                 break;
304                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
305                                 ast_frfree(f);
306                                 break;
307                         }
308                         ast_frfree(f);
309                         ichan = NULL;
310                         if(input) {
311                                 ichan = get_zap_channel_locked(input);
312                                 input = 0;
313                         }
314         
315                         tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
316                         
317                         
318                         if ( !tempchan && !lastchan )
319                                 break;
320                         if ( tempchan && tempchan->type && (!strcmp(tempchan->type, "Zap")) && (tempchan != chan) ) {
321                                 ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
322                                 strncpy(confstr, tempchan->name, sizeof(confstr) - 1);
323                                 ast_mutex_unlock(&tempchan->lock);
324                                 if ((tmp = strchr(confstr,'-'))) {
325                                         *tmp = '\0';
326                                 }
327                                 confno = atoi(strchr(confstr,'/') + 1);
328                                 ast_stopstream(chan);
329                                 ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
330                                 res = conf_run(chan, confno, confflags);
331                                 if (res<0) break;
332                                 input = res;
333                         } else if (tempchan)
334                                 ast_mutex_unlock(&tempchan->lock);
335                         lastchan = tempchan;
336         }
337         LOCAL_USER_REMOVE(u);
338         return res;
339 }
340
341 int unload_module(void)
342 {
343         STANDARD_HANGUP_LOCALUSERS;
344         return ast_unregister_application(app);
345 }
346
347 int load_module(void)
348 {
349         return ast_register_application(app, conf_exec, synopsis, descrip);
350 }
351
352 char *description(void)
353 {
354         return tdesc;
355 }
356
357 int usecount(void)
358 {
359         int res;
360         STANDARD_USECOUNT(res);
361         return res;
362 }
363
364 char *key()
365 {
366         return ASTERISK_GPL_KEY;
367 }
368