9511c4f6b964c628441ed9852dd0b3c6f7b4d72a
[asterisk/asterisk.git] / apps / app_zapbarge.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  * Special thanks to comphealth.com for sponsoring this
9  * GPL application.
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief Zap Barge support
25  * 
26  * \ingroup applications
27  */
28
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/ioctl.h>
35
36 #ifdef __linux__
37 #include <linux/zaptel.h>
38 #else
39 #include <zaptel.h>
40 #endif /* __linux__ */
41
42 #include "asterisk.h"
43
44 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45
46 #include "asterisk/lock.h"
47 #include "asterisk/file.h"
48 #include "asterisk/logger.h"
49 #include "asterisk/channel.h"
50 #include "asterisk/pbx.h"
51 #include "asterisk/module.h"
52 #include "asterisk/config.h"
53 #include "asterisk/app.h"
54 #include "asterisk/options.h"
55 #include "asterisk/cli.h"
56 #include "asterisk/say.h"
57 #include "asterisk/utils.h"
58
59 static char *tdesc = "Barge in on Zap channel application";
60
61 static char *app = "ZapBarge";
62
63 static char *synopsis = "Barge in (monitor) Zap channel";
64
65 static char *descrip = 
66 "  ZapBarge([channel]): Barges in on a specified zap\n"
67 "channel or prompts if one is not specified.  Returns\n"
68 "-1 when caller user hangs up and is independent of the\n"
69 "state of the channel being monitored.";
70
71
72 STANDARD_LOCAL_USER;
73
74 LOCAL_USER_DECL;
75
76
77 #define CONF_SIZE 160
78
79 static int careful_write(int fd, unsigned char *data, int len)
80 {
81         int res;
82         while(len) {
83                 res = write(fd, data, len);
84                 if (res < 1) {
85                         if (errno != EAGAIN) {
86                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
87                                 return -1;
88                         } else
89                                 return 0;
90                 }
91                 len -= res;
92                 data += res;
93         }
94         return 0;
95 }
96
97 static int conf_run(struct ast_channel *chan, int confno, int confflags)
98 {
99         int fd;
100         struct zt_confinfo ztc;
101         struct ast_frame *f;
102         struct ast_channel *c;
103         struct ast_frame fr;
104         int outfd;
105         int ms;
106         int nfds;
107         int res;
108         int flags;
109         int retryzap;
110         int origfd;
111         int ret = -1;
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) && (f->subclass == '#')) {
214                                 ret = 0;
215                                 break;
216                         } else if (fd != chan->fds[0]) {
217                                 if (f->frametype == AST_FRAME_VOICE) {
218                                         if (f->subclass == AST_FORMAT_ULAW) {
219                                                 /* Carefully write */
220                                                 careful_write(fd, f->data, f->datalen);
221                                         } else
222                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
223                                 }
224                         }
225                         ast_frfree(f);
226                 } else if (outfd > -1) {
227                         res = read(outfd, buf, CONF_SIZE);
228                         if (res > 0) {
229                                 memset(&fr, 0, sizeof(fr));
230                                 fr.frametype = AST_FRAME_VOICE;
231                                 fr.subclass = AST_FORMAT_ULAW;
232                                 fr.datalen = res;
233                                 fr.samples = res;
234                                 fr.data = buf;
235                                 fr.offset = AST_FRIENDLY_OFFSET;
236                                 if (ast_write(chan, &fr) < 0) {
237                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
238                                         /* break; */
239                                 }
240                         } else 
241                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
242                 }
243         }
244         if (fd != chan->fds[0])
245                 close(fd);
246         else {
247                 /* Take out of conference */
248                 /* Add us to the conference */
249                 ztc.chan = 0;   
250                 ztc.confno = 0;
251                 ztc.confmode = 0;
252                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
253                         ast_log(LOG_WARNING, "Error setting conference\n");
254                 }
255         }
256
257 outrun:
258
259         return ret;
260 }
261
262 static int conf_exec(struct ast_channel *chan, void *data)
263 {
264         int res=-1;
265         struct localuser *u;
266         int retrycnt = 0;
267         int confflags = 0;
268         int confno = 0;
269         char confstr[80] = "";
270
271         LOCAL_USER_ADD(u);
272         
273         if (!ast_strlen_zero(data)) {
274                 if ((sscanf(data, "Zap/%d", &confno) != 1) &&
275                     (sscanf(data, "%d", &confno) != 1)) {
276                         ast_log(LOG_WARNING, "ZapBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
277                         LOCAL_USER_REMOVE(u);
278                         return 0;
279                 }
280         }
281         
282         if (chan->_state != AST_STATE_UP)
283                 ast_answer(chan);
284
285         while(!confno && (++retrycnt < 4)) {
286                 /* Prompt user for conference number */
287                 confstr[0] = '\0';
288                 res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
289                 if (res <0) goto out;
290                 if (sscanf(confstr, "%d", &confno) != 1)
291                         confno = 0;
292         }
293         if (confno) {
294                 /* XXX Should prompt user for pin if pin is required XXX */
295                 /* Run the conference */
296                 res = conf_run(chan, confno, confflags);
297         }
298 out:
299         /* Do the conference */
300         LOCAL_USER_REMOVE(u);
301         return res;
302 }
303
304 int unload_module(void)
305 {
306         int res;
307         
308         res = ast_unregister_application(app);
309         
310         STANDARD_HANGUP_LOCALUSERS;
311
312         return res;     
313 }
314
315 int load_module(void)
316 {
317         return ast_register_application(app, conf_exec, synopsis, descrip);
318 }
319
320 char *description(void)
321 {
322         return tdesc;
323 }
324
325 int usecount(void)
326 {
327         int res;
328         STANDARD_USECOUNT(res);
329         return res;
330 }
331
332 char *key()
333 {
334         return ASTERISK_GPL_KEY;
335 }