AST-2009-005
[asterisk/asterisk.git] / apps / app_dahdibarge.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 DAHDI 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>dahdi</depend>
36  ***/
37
38 #include "asterisk.h"
39
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41
42 #include <dahdi/user.h>
43
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/module.h"
49 #include "asterisk/config.h"
50 #include "asterisk/app.h"
51 #include "asterisk/cli.h"
52 #include "asterisk/say.h"
53 #include "asterisk/utils.h"
54
55 /*** DOCUMENTATION
56         <application name="DAHDIBarge" language="en_US">
57                 <synopsis>
58                         Barge in (monitor) DAHDI channel.
59                 </synopsis>
60                 <syntax>
61                         <parameter name="channel">
62                                 <para>Channel to barge.</para>
63                         </parameter>
64                 </syntax>
65                 <description>
66                         <para>Barges in on a specified DAHDI <replaceable>channel</replaceable> or prompts
67                         if one is not specified. Returns <literal>-1</literal> when caller user hangs
68                         up and is independent of the state of the channel being monitored.
69                         </para>
70                 </description>
71         </application>
72  ***/
73 static const char app[] = "DAHDIBarge";
74
75 #define CONF_SIZE 160
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 dahdi_confinfo dahdic;
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 retrydahdi;
108         int origfd;
109         int ret = -1;
110
111         struct dahdi_bufferinfo bi;
112         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
113         char *buf = __buf + AST_FRIENDLY_OFFSET;
114
115         /* Set it into U-law mode (write) */
116         if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
117                 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
118                 goto outrun;
119         }
120
121         /* Set it into U-law mode (read) */
122         if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
123                 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
124                 goto outrun;
125         }
126         ast_indicate(chan, -1);
127         retrydahdi = strcasecmp(chan->tech->type, "DAHDI");
128 dahdiretry:
129         origfd = chan->fds[0];
130         if (retrydahdi) {
131                 fd = open("/dev/dahdi/pseudo", O_RDWR);
132                 if (fd < 0) {
133                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
134                         goto outrun;
135                 }
136                 /* Make non-blocking */
137                 flags = fcntl(fd, F_GETFL);
138                 if (flags < 0) {
139                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
140                         close(fd);
141                         goto outrun;
142                 }
143                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
144                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
145                         close(fd);
146                         goto outrun;
147                 }
148                 /* Setup buffering information */
149                 memset(&bi, 0, sizeof(bi));
150                 bi.bufsize = CONF_SIZE;
151                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
152                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
153                 bi.numbufs = 4;
154                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
155                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
156                         close(fd);
157                         goto outrun;
158                 }
159                 nfds = 1;
160         } else {
161                 /* XXX Make sure we're not running on a pseudo channel XXX */
162                 fd = chan->fds[0];
163                 nfds = 0;
164         }
165         memset(&dahdic, 0, sizeof(dahdic));
166         /* Check to see if we're in a conference... */
167         dahdic.chan = 0;        
168         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
169                 ast_log(LOG_WARNING, "Error getting conference\n");
170                 close(fd);
171                 goto outrun;
172         }
173         if (dahdic.confmode) {
174                 /* Whoa, already in a conference...  Retry... */
175                 if (!retrydahdi) {
176                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
177                         retrydahdi = 1;
178                         goto dahdiretry;
179                 }
180         }
181         memset(&dahdic, 0, sizeof(dahdic));
182         /* Add us to the conference */
183         dahdic.chan = 0;        
184         dahdic.confno = confno;
185         dahdic.confmode = DAHDI_CONF_MONITORBOTH;
186
187         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
188                 ast_log(LOG_WARNING, "Error setting conference\n");
189                 close(fd);
190                 goto outrun;
191         }
192         ast_debug(1, "Placed channel %s in DAHDI channel %d monitor\n", chan->name, confno);
193
194         for(;;) {
195                 outfd = -1;
196                 ms = -1;
197                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
198                 if (c) {
199                         if (c->fds[0] != origfd) {
200                                 if (retrydahdi) {
201                                         /* Kill old pseudo */
202                                         close(fd);
203                                 }
204                                 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
205                                 retrydahdi = 0;
206                                 goto dahdiretry;
207                         }
208                         f = ast_read(c);
209                         if (!f) 
210                                 break;
211                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
212                                 ret = 0;
213                                 ast_frfree(f);
214                                 break;
215                         } else if (fd != chan->fds[0]) {
216                                 if (f->frametype == AST_FRAME_VOICE) {
217                                         if (f->subclass == AST_FORMAT_ULAW) {
218                                                 /* Carefully write */
219                                                 careful_write(fd, f->data.ptr, f->datalen);
220                                         } else
221                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
222                                 }
223                         }
224                         ast_frfree(f);
225                 } else if (outfd > -1) {
226                         res = read(outfd, buf, CONF_SIZE);
227                         if (res > 0) {
228                                 memset(&fr, 0, sizeof(fr));
229                                 fr.frametype = AST_FRAME_VOICE;
230                                 fr.subclass = AST_FORMAT_ULAW;
231                                 fr.datalen = res;
232                                 fr.samples = res;
233                                 fr.data.ptr = buf;
234                                 fr.offset = AST_FRIENDLY_OFFSET;
235                                 if (ast_write(chan, &fr) < 0) {
236                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
237                                         /* break; */
238                                 }
239                         } else 
240                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
241                 }
242         }
243         if (fd != chan->fds[0])
244                 close(fd);
245         else {
246                 /* Take out of conference */
247                 /* Add us to the conference */
248                 dahdic.chan = 0;        
249                 dahdic.confno = 0;
250                 dahdic.confmode = 0;
251                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
252                         ast_log(LOG_WARNING, "Error setting conference\n");
253                 }
254         }
255
256 outrun:
257
258         return ret;
259 }
260
261 static int conf_exec(struct ast_channel *chan, const char *data)
262 {
263         int res = -1;
264         int retrycnt = 0;
265         int confflags = 0;
266         int confno = 0;
267         char confnostr[80] = "";
268         
269         if (!ast_strlen_zero(data)) {
270                 if ((sscanf(data, "DAHDI/%30d", &confno) != 1) &&
271                     (sscanf(data, "%30d", &confno) != 1)) {
272                         ast_log(LOG_WARNING, "DAHDIBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
273                         return 0;
274                 }
275         }
276         
277         if (chan->_state != AST_STATE_UP)
278                 ast_answer(chan);
279
280         while(!confno && (++retrycnt < 4)) {
281                 /* Prompt user for conference number */
282                 confnostr[0] = '\0';
283                 res = ast_app_getdata(chan, "conf-getchannel",confnostr, sizeof(confnostr) - 1, 0);
284                 if (res <0) goto out;
285                 if (sscanf(confnostr, "%30d", &confno) != 1)
286                         confno = 0;
287         }
288         if (confno) {
289                 /* XXX Should prompt user for pin if pin is required XXX */
290                 /* Run the conference */
291                 res = conf_run(chan, confno, confflags);
292         }
293 out:
294         /* Do the conference */
295         return res;
296 }
297
298 static int unload_module(void)
299 {
300         return ast_unregister_application(app);
301 }
302
303 static int load_module(void)
304 {
305         return ((ast_register_application_xml(app, conf_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
306 }
307
308 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on DAHDI channel application");