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