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