major redesign of the channel spy infrastructure, increasing efficiency and reducing...
[asterisk/asterisk.git] / apps / app_chanspy.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5  *
6  * Disclaimed to Digium
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  * \brief ChanSpy: Listen in on any channel.
21  * 
22  */
23
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/file.h"
34 #include "asterisk/logger.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/features.h"
37 #include "asterisk/options.h"
38 #include "asterisk/app.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/say.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/translate.h"
43 #include "asterisk/module.h"
44 #include "asterisk/lock.h"
45
46 AST_MUTEX_DEFINE_STATIC(modlock);
47
48 #define AST_NAME_STRLEN 256
49 #define ALL_DONE(u, ret) LOCAL_USER_REMOVE(u); return ret;
50 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
51
52 static const char *synopsis = "Tap into any type of asterisk channel and listen to audio";
53 static const char *app = "ChanSpy";
54 static const char *desc = "   Chanspy([<scanspec>][|<options>])\n\n"
55 "Valid Options:\n"
56 " - q: quiet, don't announce channels beep, etc.\n"
57 " - b: bridged, only spy on channels involved in a bridged call.\n"
58 " - v([-4..4]): adjust the initial volume. (negative is quieter)\n"
59 " - g(grp): enforce group.  Match only calls where their ${SPYGROUP} is 'grp'.\n"
60 " - r[(basename)]: Record session to monitor spool dir (with optional basename, default is 'chanspy')\n\n"
61 "If <scanspec> is specified, only channel names *beginning* with that string will be scanned.\n"
62 "('all' or an empty string are also both valid <scanspec>)\n\n"
63 "While Spying:\n\n"
64 "Dialing # cycles the volume level.\n"
65 "Dialing * will stop spying and look for another channel to spy on.\n"
66 "Dialing a series of digits followed by # builds a channel name to append to <scanspec>\n"
67 "(e.g. run Chanspy(Agent) and dial 1234# while spying to jump to channel Agent/1234)\n\n"
68 "";
69
70 static const char *chanspy_spy_type = "ChanSpy";
71
72 #define OPTION_QUIET     (1 << 0)       /* Quiet, no announcement */
73 #define OPTION_BRIDGED   (1 << 1)       /* Only look at bridged calls */
74 #define OPTION_VOLUME    (1 << 2)       /* Specify initial volume */
75 #define OPTION_GROUP     (1 << 3)   /* Only look at channels in group */
76 #define OPTION_RECORD    (1 << 4)   /* Record */
77
78 AST_DECLARE_OPTIONS(chanspy_opts,{
79         ['q'] = { OPTION_QUIET },
80         ['b'] = { OPTION_BRIDGED },
81         ['v'] = { OPTION_VOLUME, 1 },
82         ['g'] = { OPTION_GROUP, 2 },
83         ['r'] = { OPTION_RECORD, 3 },
84 });
85
86 STANDARD_LOCAL_USER;
87 LOCAL_USER_DECL;
88
89 struct chanspy_translation_helper {
90         /* spy data */
91         struct ast_channel_spy spy;
92         int fd;
93         int volfactor;
94 };
95
96 static struct ast_channel *local_channel_walk(struct ast_channel *chan) 
97 {
98         struct ast_channel *ret;
99         ast_mutex_lock(&modlock);       
100         if ((ret = ast_channel_walk_locked(chan))) {
101                 ast_mutex_unlock(&ret->lock);
102         }
103         ast_mutex_unlock(&modlock);                     
104         return ret;
105 }
106
107 static struct ast_channel *local_get_channel_begin_name(char *name) 
108 {
109         struct ast_channel *chan, *ret = NULL;
110         ast_mutex_lock(&modlock);
111         chan = local_channel_walk(NULL);
112         while (chan) {
113                 if (!strncmp(chan->name, name, strlen(name))) {
114                         ret = chan;
115                         break;
116                 }
117                 chan = local_channel_walk(chan);
118         }
119         ast_mutex_unlock(&modlock);
120         
121         return ret;
122 }
123
124 static void *spy_alloc(struct ast_channel *chan, void *data)
125 {
126         /* just store the data pointer in the channel structure */
127         return data;
128 }
129
130 static void spy_release(struct ast_channel *chan, void *data)
131 {
132         /* nothing to do */
133 }
134
135 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) 
136 {
137         struct chanspy_translation_helper *csth = data;
138         struct ast_frame *f;
139                 
140         if (csth->spy.status != CHANSPY_RUNNING)
141                 /* Channel is already gone more than likely */
142                 return -1;
143
144         ast_mutex_lock(&csth->spy.lock);
145         f = ast_channel_spy_read_frame(&csth->spy, samples);
146         ast_mutex_unlock(&csth->spy.lock);
147                 
148         if (!f)
149                 return 0;
150                 
151         if (ast_write(chan, f)) {
152                 ast_frfree(f);
153                 return -1;
154         }
155
156         if (csth->fd)
157                 write(csth->fd, f->data, f->datalen);
158
159         ast_frfree(f);
160
161         return 0;
162 }
163
164
165 static struct ast_generator spygen = {
166         .alloc = spy_alloc,
167         .release = spy_release,
168         .generate = spy_generate, 
169 };
170
171 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy) 
172 {
173         int res;
174         struct ast_channel *peer;
175
176         ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
177
178         ast_mutex_lock(&chan->lock);
179         res = ast_channel_spy_add(chan, spy);
180         ast_mutex_unlock(&chan->lock);
181
182         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
183                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
184         }
185
186         return res;
187 }
188
189 static void stop_spying(struct ast_channel *chan, struct ast_channel_spy *spy) 
190 {
191         /* If our status has changed, then the channel we're spying on is gone....
192            DON'T TOUCH IT!!!  RUN AWAY!!! */
193         if (spy->status != CHANSPY_RUNNING)
194                 return;
195
196         if (!chan)
197                 return;
198
199         ast_mutex_lock(&chan->lock);
200         ast_channel_spy_remove(chan, spy);
201         ast_mutex_unlock(&chan->lock);
202 };
203
204 /* Map 'volume' levels from -4 through +4 into
205    decibel (dB) settings for channel drivers
206 */
207 static signed char volfactor_map[] = {
208         -24,
209         -18,
210         -12,
211         -6,
212         0,
213         6,
214         12,
215         18,
216         24,
217 };
218
219 /* attempt to set the desired gain adjustment via the channel driver;
220    if successful, clear it out of the csth structure so the
221    generator will not attempt to do the adjustment itself
222 */
223 static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
224 {
225         signed char volume_adjust = volfactor_map[csth->volfactor + 4];
226
227         if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0))
228                 csth->volfactor = 0;
229 }
230
231 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd) 
232 {
233         struct chanspy_translation_helper csth;
234         int running, res = 0, x = 0;
235         char inp[24];
236         char *name=NULL;
237         struct ast_frame *f;
238
239         running = (chan && !ast_check_hangup(chan) && spyee && !ast_check_hangup(spyee));
240
241         if (running) {
242                 memset(inp, 0, sizeof(inp));
243                 name = ast_strdupa(spyee->name);
244                 if (option_verbose >= 2)
245                         ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
246
247                 memset(&csth, 0, sizeof(csth));
248                 ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
249                 ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
250                 ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
251                 csth.spy.type = chanspy_spy_type;
252                 csth.spy.status = CHANSPY_RUNNING;
253                 csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
254                 csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
255                 ast_mutex_init(&csth.spy.lock);
256                 csth.volfactor = *volfactor;
257                 set_volume(chan, &csth);
258                 csth.spy.read_vol_adjustment = csth.volfactor;
259                 csth.spy.write_vol_adjustment = csth.volfactor;
260                 csth.fd = fd;
261
262                 if (start_spying(spyee, chan, &csth.spy))
263                         running = 0;
264         }
265
266         if (running) {
267                 running = 1;
268                 ast_activate_generator(chan, &spygen, &csth);
269
270                 while (csth.spy.status == CHANSPY_RUNNING &&
271                        chan && !ast_check_hangup(chan) &&
272                        spyee &&
273                        !ast_check_hangup(spyee) &&
274                        running == 1 &&
275                        (res = ast_waitfor(chan, -1) > -1)) {
276                         if ((f = ast_read(chan))) {
277                                 res = 0;
278                                 if (f->frametype == AST_FRAME_DTMF) {
279                                         res = f->subclass;
280                                 }
281                                 ast_frfree(f);
282                                 if (!res) {
283                                         continue;
284                                 }
285                         } else {
286                                 break;
287                         }
288                         if (x == sizeof(inp)) {
289                                 x = 0;
290                         }
291                         if (res < 0) {
292                                 running = -1;
293                         }
294                         if (res == 0) {
295                                 continue;
296                         } else if (res == '*') {
297                                 running = 0; 
298                         } else if (res == '#') {
299                                 if (!ast_strlen_zero(inp)) {
300                                         running = x ? atoi(inp) : -1;
301                                         break;
302                                 } else {
303                                         (*volfactor)++;
304                                         if (*volfactor > 4) {
305                                                 *volfactor = -4;
306                                         }
307                                         if (option_verbose > 2) {
308                                                 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
309                                         }
310                                         csth.volfactor = *volfactor;
311                                         set_volume(chan, &csth);
312                                         csth.spy.read_vol_adjustment = csth.volfactor;
313                                         csth.spy.write_vol_adjustment = csth.volfactor;
314                                 }
315                         } else if (res >= 48 && res <= 57) {
316                                 inp[x++] = res;
317                         }
318                 }
319                 ast_deactivate_generator(chan);
320                 stop_spying(spyee, &csth.spy);
321
322                 if (option_verbose >= 2) {
323                         ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
324                 }
325         } else {
326                 running = 0;
327         }
328
329         ast_mutex_destroy(&csth.spy.lock);
330
331         return running;
332 }
333
334 static int chanspy_exec(struct ast_channel *chan, void *data)
335 {
336         struct localuser *u;
337         struct ast_channel *peer=NULL, *prev=NULL;
338         char name[AST_NAME_STRLEN],
339                 peer_name[AST_NAME_STRLEN + 5],
340                 *args,
341                 *ptr = NULL,
342                 *options = NULL,
343                 *spec = NULL,
344                 *argv[5],
345                 *mygroup = NULL,
346                 *recbase = NULL;
347         int res = -1,
348                 volfactor = 0,
349                 silent = 0,
350                 argc = 0,
351                 bronly = 0,
352                 chosen = 0,
353                 count=0,
354                 waitms = 100,
355                 num = 0,
356                 oldrf = 0,
357                 oldwf = 0,
358                 fd = 0;
359         struct ast_flags flags;
360         signed char zero_volume = 0;
361
362         if (!(args = ast_strdupa((char *)data))) {
363                 ast_log(LOG_ERROR, "Out of memory!\n");
364                 return -1;
365         }
366
367         LOCAL_USER_ADD(u);
368
369         oldrf = chan->readformat;
370         oldwf = chan->writeformat;
371         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
372                 ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
373                 LOCAL_USER_REMOVE(u);
374                 return -1;
375         }
376         
377         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
378                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
379                 LOCAL_USER_REMOVE(u);
380                 return -1;
381         }
382
383         ast_answer(chan);
384
385         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
386
387         if ((argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
388                 spec = argv[0];
389                 if ( argc > 1) {
390                         options = argv[1];
391                 }
392                 if (ast_strlen_zero(spec) || !strcmp(spec, "all")) {
393                         spec = NULL;
394                 }
395         }
396         
397         if (options) {
398                 char *opts[3];
399                 ast_parseoptions(chanspy_opts, &flags, opts, options);
400                 if (ast_test_flag(&flags, OPTION_GROUP)) {
401                         mygroup = opts[1];
402                 }
403                 if (ast_test_flag(&flags, OPTION_RECORD)) {
404                         if (!(recbase = opts[2])) {
405                                 recbase = "chanspy";
406                         }
407                 }
408                 silent = ast_test_flag(&flags, OPTION_QUIET);
409                 bronly = ast_test_flag(&flags, OPTION_BRIDGED);
410                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[1]) {
411                         int vol;
412
413                         if ((sscanf(opts[0], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
414                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
415                         else
416                                 volfactor = vol;
417                         }
418         }
419
420         if (recbase) {
421                 char filename[512];
422                 snprintf(filename,sizeof(filename),"%s/%s.%ld.raw",ast_config_AST_MONITOR_DIR, recbase, time(NULL));
423                 if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC)) <= 0) {
424                         ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename);
425                         fd = 0;
426                 }
427         }
428
429         for(;;) {
430                 if (!silent) {
431                         res = ast_streamfile(chan, "beep", chan->language);
432                         if (!res)
433                                 res = ast_waitstream(chan, "");
434                         if (res < 0) {
435                                 ast_clear_flag(chan, AST_FLAG_SPYING);
436                                 break;
437                         }
438                 }
439
440                 count = 0;
441                 res = ast_waitfordigit(chan, waitms);
442                 if (res < 0) {
443                         ast_clear_flag(chan, AST_FLAG_SPYING);
444                         break;
445                 }
446                                 
447                 peer = local_channel_walk(NULL);
448                 prev=NULL;
449                 while(peer) {
450                         if (peer != chan) {
451                                 char *group = NULL;
452                                 int igrp = 1;
453
454                                 if (peer == prev && !chosen) {
455                                         break;
456                                 }
457                                 chosen = 0;
458                                 group = pbx_builtin_getvar_helper(peer, "SPYGROUP");
459                                 if (mygroup) {
460                                         if (!group || strcmp(mygroup, group)) {
461                                                 igrp = 0;
462                                         }
463                                 }
464                                 
465                                 if (igrp && (!spec || ((strlen(spec) < strlen(peer->name) &&
466                                                         !strncasecmp(peer->name, spec, strlen(spec)))))) {
467                                         if (peer && (!bronly || ast_bridged_channel(peer)) &&
468                                             !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
469                                                 int x = 0;
470                                                 strncpy(peer_name, "spy-", 5);
471                                                 strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN);
472                                                 ptr = strchr(peer_name, '/');
473                                                 *ptr = '\0';
474                                                 ptr++;
475                                                 for (x = 0 ; x < strlen(peer_name) ; x++) {
476                                                         if (peer_name[x] == '/') {
477                                                                 break;
478                                                         }
479                                                         peer_name[x] = tolower(peer_name[x]);
480                                                 }
481
482                                                 if (!silent) {
483                                                         if (ast_fileexists(peer_name, NULL, NULL) != -1) {
484                                                                 res = ast_streamfile(chan, peer_name, chan->language);
485                                                                 if (!res)
486                                                                         res = ast_waitstream(chan, "");
487                                                                 if (res)
488                                                                         break;
489                                                         } else
490                                                                 res = ast_say_character_str(chan, peer_name, "", chan->language);
491                                                         if ((num=atoi(ptr))) 
492                                                                 ast_say_digits(chan, atoi(ptr), "", chan->language);
493                                                 }
494                                                 count++;
495                                                 prev = peer;
496                                                 res = channel_spy(chan, peer, &volfactor, fd);
497                                                 if (res == -1) {
498                                                         break;
499                                                 } else if (res > 1 && spec) {
500                                                         snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res);
501                                                         if ((peer = local_get_channel_begin_name(name))) {
502                                                                 chosen = 1;
503                                                         }
504                                                         continue;
505                                                 }
506                                         }
507                                 }
508                         }
509                         if ((peer = local_channel_walk(peer)) == NULL) {
510                                 break;
511                         }
512                 }
513                 waitms = count ? 100 : 5000;
514         }
515         
516
517         if (fd > 0) {
518                 close(fd);
519         }
520
521         if (oldrf && ast_set_read_format(chan, oldrf) < 0) {
522                 ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
523         }
524         
525         if (oldwf && ast_set_write_format(chan, oldwf) < 0) {
526                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
527         }
528
529         ast_clear_flag(chan, AST_FLAG_SPYING);
530
531         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
532
533         ALL_DONE(u, res);
534 }
535
536 int unload_module(void)
537 {
538         int res;
539
540         res = ast_unregister_application(app);
541
542         STANDARD_HANGUP_LOCALUSERS;
543
544         return res;
545 }
546
547 int load_module(void)
548 {
549         return ast_register_application(app, chanspy_exec, synopsis, desc);
550 }
551
552 char *description(void)
553 {
554         return (char *) synopsis;
555 }
556
557 int usecount(void)
558 {
559         int res;
560         STANDARD_USECOUNT(res);
561         return res;
562 }
563
564 char *key()
565 {
566         return ASTERISK_GPL_KEY;
567 }