Applications no longer need to call ast_module_user_add and ast_module_user_remove...
[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  * \author Mark Spencer <markster@digium.com>
27  *
28  * \note Special thanks to comphealth.com for sponsoring this
29  * GPL application.
30  * 
31  * \ingroup applications
32  */
33
34 /*** MODULEINFO
35         <depend>zaptel</depend>
36  ***/
37
38 #include "asterisk.h"
39
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include "asterisk/zapata.h"
48
49 #include "asterisk/lock.h"
50 #include "asterisk/file.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/module.h"
55 #include "asterisk/config.h"
56 #include "asterisk/app.h"
57 #include "asterisk/options.h"
58 #include "asterisk/cli.h"
59 #include "asterisk/say.h"
60 #include "asterisk/utils.h"
61
62 static char *app = "ZapBarge";
63
64 static char *synopsis = "Barge in (monitor) Zap channel";
65
66 static char *descrip = 
67 "  ZapBarge([channel]): Barges in on a specified zap\n"
68 "channel or prompts if one is not specified.  Returns\n"
69 "-1 when caller user hangs up and is independent of the\n"
70 "state of the channel being monitored.";
71
72
73 #define CONF_SIZE 160
74
75 static int careful_write(int fd, unsigned char *data, int len)
76 {
77         int res;
78         while(len) {
79                 res = write(fd, data, len);
80                 if (res < 1) {
81                         if (errno != EAGAIN) {
82                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
83                                 return -1;
84                         } else
85                                 return 0;
86                 }
87                 len -= res;
88                 data += res;
89         }
90         return 0;
91 }
92
93 static int conf_run(struct ast_channel *chan, int confno, int confflags)
94 {
95         int fd;
96         struct zt_confinfo ztc;
97         struct ast_frame *f;
98         struct ast_channel *c;
99         struct ast_frame fr;
100         int outfd;
101         int ms;
102         int nfds;
103         int res;
104         int flags;
105         int retryzap;
106         int origfd;
107         int ret = -1;
108
109         ZT_BUFFERINFO bi;
110         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
111         char *buf = __buf + AST_FRIENDLY_OFFSET;
112
113         /* Set it into U-law mode (write) */
114         if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
115                 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
116                 goto outrun;
117         }
118
119         /* Set it into U-law mode (read) */
120         if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
121                 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
122                 goto outrun;
123         }
124         ast_indicate(chan, -1);
125         retryzap = strcasecmp(chan->tech->type, "Zap");
126 zapretry:
127         origfd = chan->fds[0];
128         if (retryzap) {
129                 fd = open("/dev/zap/pseudo", O_RDWR);
130                 if (fd < 0) {
131                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
132                         goto outrun;
133                 }
134                 /* Make non-blocking */
135                 flags = fcntl(fd, F_GETFL);
136                 if (flags < 0) {
137                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
138                         close(fd);
139                         goto outrun;
140                 }
141                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
142                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
143                         close(fd);
144                         goto outrun;
145                 }
146                 /* Setup buffering information */
147                 memset(&bi, 0, sizeof(bi));
148                 bi.bufsize = CONF_SIZE;
149                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
150                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
151                 bi.numbufs = 4;
152                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
153                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
154                         close(fd);
155                         goto outrun;
156                 }
157                 nfds = 1;
158         } else {
159                 /* XXX Make sure we're not running on a pseudo channel XXX */
160                 fd = chan->fds[0];
161                 nfds = 0;
162         }
163         memset(&ztc, 0, sizeof(ztc));
164         /* Check to see if we're in a conference... */
165         ztc.chan = 0;   
166         if (ioctl(fd, ZT_GETCONF, &ztc)) {
167                 ast_log(LOG_WARNING, "Error getting conference\n");
168                 close(fd);
169                 goto outrun;
170         }
171         if (ztc.confmode) {
172                 /* Whoa, already in a conference...  Retry... */
173                 if (!retryzap) {
174                         ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
175                         retryzap = 1;
176                         goto zapretry;
177                 }
178         }
179         memset(&ztc, 0, sizeof(ztc));
180         /* Add us to the conference */
181         ztc.chan = 0;   
182         ztc.confno = confno;
183         ztc.confmode = ZT_CONF_MONITORBOTH;
184
185         if (ioctl(fd, ZT_SETCONF, &ztc)) {
186                 ast_log(LOG_WARNING, "Error setting conference\n");
187                 close(fd);
188                 goto outrun;
189         }
190         ast_debug(1, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
191
192         for(;;) {
193                 outfd = -1;
194                 ms = -1;
195                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
196                 if (c) {
197                         if (c->fds[0] != origfd) {
198                                 if (retryzap) {
199                                         /* Kill old pseudo */
200                                         close(fd);
201                                 }
202                                 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
203                                 retryzap = 0;
204                                 goto zapretry;
205                         }
206                         f = ast_read(c);
207                         if (!f) 
208                                 break;
209                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
210                                 ret = 0;
211                                 ast_frfree(f);
212                                 break;
213                         } else if (fd != chan->fds[0]) {
214                                 if (f->frametype == AST_FRAME_VOICE) {
215                                         if (f->subclass == AST_FORMAT_ULAW) {
216                                                 /* Carefully write */
217                                                 careful_write(fd, f->data, f->datalen);
218                                         } else
219                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
220                                 }
221                         }
222                         ast_frfree(f);
223                 } else if (outfd > -1) {
224                         res = read(outfd, buf, CONF_SIZE);
225                         if (res > 0) {
226                                 memset(&fr, 0, sizeof(fr));
227                                 fr.frametype = AST_FRAME_VOICE;
228                                 fr.subclass = AST_FORMAT_ULAW;
229                                 fr.datalen = res;
230                                 fr.samples = res;
231                                 fr.data = buf;
232                                 fr.offset = AST_FRIENDLY_OFFSET;
233                                 if (ast_write(chan, &fr) < 0) {
234                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
235                                         /* break; */
236                                 }
237                         } else 
238                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
239                 }
240         }
241         if (fd != chan->fds[0])
242                 close(fd);
243         else {
244                 /* Take out of conference */
245                 /* Add us to the conference */
246                 ztc.chan = 0;   
247                 ztc.confno = 0;
248                 ztc.confmode = 0;
249                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
250                         ast_log(LOG_WARNING, "Error setting conference\n");
251                 }
252         }
253
254 outrun:
255
256         return ret;
257 }
258
259 static int conf_exec(struct ast_channel *chan, void *data)
260 {
261         int res=-1;
262         int retrycnt = 0;
263         int confflags = 0;
264         int confno = 0;
265         char confstr[80] = "";
266         
267         if (!ast_strlen_zero(data)) {
268                 if ((sscanf(data, "Zap/%d", &confno) != 1) &&
269                     (sscanf(data, "%d", &confno) != 1)) {
270                         ast_log(LOG_WARNING, "ZapBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
271                         return 0;
272                 }
273         }
274         
275         if (chan->_state != AST_STATE_UP)
276                 ast_answer(chan);
277
278         while(!confno && (++retrycnt < 4)) {
279                 /* Prompt user for conference number */
280                 confstr[0] = '\0';
281                 res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
282                 if (res <0) goto out;
283                 if (sscanf(confstr, "%d", &confno) != 1)
284                         confno = 0;
285         }
286         if (confno) {
287                 /* XXX Should prompt user for pin if pin is required XXX */
288                 /* Run the conference */
289                 res = conf_run(chan, confno, confflags);
290         }
291 out:
292         /* Do the conference */
293         return res;
294 }
295
296 static int unload_module(void)
297 {
298         return ast_unregister_application(app);
299 }
300
301 static int load_module(void)
302 {
303         return ast_register_application(app, conf_exec, synopsis, descrip);
304 }
305
306 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on Zap channel application");