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