95721b871f8c4663fc2054edd6bc528c52480751
[asterisk/asterisk.git] / apps / app_zapscan.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Modified from app_zapbarge by David Troy <dave@toad.net>
9  *
10  * Special thanks to comphealth.com for sponsoring this
11  * GPL application.
12  *
13  * See http://www.asterisk.org for more information about
14  * the Asterisk project. Please do not directly contact
15  * any of the maintainers of this project for assistance;
16  * the project provides a web site, mailing lists and IRC
17  * channels for your use.
18  *
19  * This program is free software, distributed under the terms of
20  * the GNU General Public License Version 2. See the LICENSE file
21  * at the top of the source tree.
22  */
23
24 /*! \file
25  *
26  * \brief Zap Scanner
27  *
28  * \author Mark Spencer <markster@digium.com>
29  *
30  * \ingroup applications
31  */
32
33 /*** MODULEINFO
34         <depend>zaptel</depend>
35  ***/
36
37 #include "asterisk.h"
38
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40
41 #include <errno.h>
42
43 #include "asterisk/zapata.h"
44
45 #include "asterisk/lock.h"
46 #include "asterisk/file.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/module.h"
51 #include "asterisk/config.h"
52 #include "asterisk/app.h"
53 #include "asterisk/options.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/cli.h"
56 #include "asterisk/say.h"
57
58 static char *app = "ZapScan";
59
60 static char *synopsis = "Scan Zap channels to monitor calls";
61
62 static char *descrip =
63 "  ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
64 "a convenient way.  Use '#' to select the next channel and use '*' to exit\n"
65 "Limit scanning to a channel GROUP by setting the option group argument.\n";
66
67
68 #define CONF_SIZE 160
69
70 static struct ast_channel *get_zap_channel_locked(int num) {
71         char name[80];
72         
73         snprintf(name,sizeof(name),"Zap/%d-1",num);
74         return ast_get_channel_by_name_locked(name);
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->tech->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_debug(1, "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_debug(1, "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_debug(1, "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_verb(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 (f)
264                 ast_frfree(f);
265         if (fd != chan->fds[0])
266                         close(fd);
267         else {
268                         /* Take out of conference */
269                         /* Add us to the conference */
270                         ztc.chan = 0;
271                         ztc.confno = 0;
272                         ztc.confmode = 0;
273                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
274                                 ast_log(LOG_WARNING, "Error setting conference\n");
275                 }
276         }
277                 
278  outrun:
279                 
280         return ret;
281 }
282
283 static int conf_exec(struct ast_channel *chan, void *data)
284 {
285         int res=-1;
286         int confflags = 0;
287         int confno = 0;
288         char confstr[80] = "", *tmp = NULL;
289         struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
290         struct ast_frame *f;
291         char *desired_group;
292         int input=0,search_group=0;
293                 
294         if (chan->_state != AST_STATE_UP)
295                 ast_answer(chan);
296         
297         desired_group = ast_strdupa(data);
298         if(!ast_strlen_zero(desired_group)) {
299                 ast_verb(3, "Scanning for group %s\n", desired_group);
300                 search_group = 1;
301         }
302
303         for (;;) {
304                 if (ast_waitfor(chan, 100) < 0)
305                         break;
306                 
307                 f = ast_read(chan);
308                 if (!f)
309                         break;
310                 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
311                         ast_frfree(f);
312                         break;
313                 }
314                 ast_frfree(f);
315                 ichan = NULL;
316                 if(input) {
317                         ichan = get_zap_channel_locked(input);
318                         input = 0;
319                 }
320                 
321                 tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
322                 
323                 if ( !tempchan && !lastchan )
324                         break;
325                 
326                 if (tempchan && search_group) {
327                         const char *mygroup;
328                         if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
329                                 ast_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
330                         } else {
331                                 ast_channel_unlock(tempchan);
332                                 lastchan = tempchan;
333                                 continue;
334                         }
335                 }
336                 if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
337                         ast_verb(3, "Zap channel %s is in-use, monitoring...\n", tempchan->name);
338                         ast_copy_string(confstr, tempchan->name, sizeof(confstr));
339                         ast_channel_unlock(tempchan);
340                         if ((tmp = strchr(confstr,'-'))) {
341                                 *tmp = '\0';
342                         }
343                         confno = atoi(strchr(confstr,'/') + 1);
344                         ast_stopstream(chan);
345                         ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
346                         res = conf_run(chan, confno, confflags);
347                         if (res<0) break;
348                         input = res;
349                 } else if (tempchan)
350                         ast_channel_unlock(tempchan);
351                 lastchan = tempchan;
352         }
353         return res;
354 }
355
356 static int unload_module(void)
357 {
358         return ast_unregister_application(app);
359 }
360
361 static int load_module(void)
362 {
363         return ast_register_application(app, conf_exec, synopsis, descrip);
364 }
365
366 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");
367