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