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