2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2006, Digium, Inc.
7 * A license has been granted to Digium (via disclaimer) for the use of
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
23 * \brief ChanSpy: Listen in on any channel.
25 * \author Anthony Minessale II <anthmct@yahoo.com>
27 * \ingroup applications
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
37 #include "asterisk/file.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/audiohook.h"
40 #include "asterisk/features.h"
41 #include "asterisk/app.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/say.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/translate.h"
46 #include "asterisk/module.h"
47 #include "asterisk/lock.h"
49 #define AST_NAME_STRLEN 256
51 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
52 static const char *app_chan = "ChanSpy";
53 static const char *desc_chan =
54 " ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
55 "audio from an Asterisk channel. This includes the audio coming in and\n"
56 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
57 "only channels beginning with this string will be spied upon.\n"
58 " While spying, the following actions may be performed:\n"
59 " - Dialing # cycles the volume level.\n"
60 " - Dialing * will stop spying and look for another channel to spy on.\n"
61 " - Dialing a series of digits followed by # builds a channel name to append\n"
62 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
63 " the digits '1234#' while spying will begin spying on the channel\n"
65 " Note: The X option supersedes the three features above in that if a valid\n"
66 " single digit extension exists in the correct context ChanSpy will\n"
67 " exit to it. This also disables choosing a channel based on 'chanprefix'\n"
68 " and a digit sequence.\n"
70 " b - Only spy on channels involved in a bridged call.\n"
71 " g(grp) - Match only channels where their SPYGROUP variable is set to\n"
72 " contain 'grp' in an optional : delimited list.\n"
73 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
74 " selected channel name.\n"
75 " r[(basename)] - Record the session to the monitor spool directory. An\n"
76 " optional base for the filename may be specified. The\n"
77 " default is 'chanspy'.\n"
78 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
79 " negative value refers to a quieter setting.\n"
80 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
81 " the spied-on channel.\n"
82 " W - Enable 'private whisper' mode, so the spying channel can\n"
83 " talk to the spied-on channel but cannot listen to that\n"
85 " o - Only listen to audio coming from this channel.\n"
86 " X - Allow the user to exit ChanSpy to a valid single digit\n"
87 " numeric extension in the current context or the context\n"
88 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
89 " name of the last channel that was spied on will be stored\n"
90 " in the SPY_CHANNEL variable.\n"
93 static const char *app_ext = "ExtenSpy";
94 static const char *desc_ext =
95 " ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
96 "audio from an Asterisk channel. This includes the audio coming in and\n"
97 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
98 "specified extension will be selected for spying. If the optional context is not\n"
99 "supplied, the current channel's context will be used.\n"
100 " While spying, the following actions may be performed:\n"
101 " - Dialing # cycles the volume level.\n"
102 " - Dialing * will stop spying and look for another channel to spy on.\n"
103 " Note: The X option superseeds the two features above in that if a valid\n"
104 " single digit extension exists in the correct context it ChanSpy will\n"
107 " b - Only spy on channels involved in a bridged call.\n"
108 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
109 " contain 'grp' in an optional : delimited list.\n"
110 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
111 " selected channel name.\n"
112 " r[(basename)] - Record the session to the monitor spool directory. An\n"
113 " optional base for the filename may be specified. The\n"
114 " default is 'chanspy'.\n"
115 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
116 " negative value refers to a quieter setting.\n"
117 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
118 " the spied-on channel.\n"
119 " W - Enable 'private whisper' mode, so the spying channel can\n"
120 " talk to the spied-on channel but cannot listen to that\n"
122 " o - Only listen to audio coming from this channel.\n"
123 " X - Allow the user to exit ChanSpy to a valid single digit\n"
124 " numeric extension in the current context or the context\n"
125 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
126 " name of the last channel that was spied on will be stored\n"
127 " in the SPY_CHANNEL variable.\n"
131 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
132 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
133 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
134 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
135 OPTION_RECORD = (1 << 4),
136 OPTION_WHISPER = (1 << 5),
137 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
138 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
139 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
149 AST_APP_OPTIONS(spy_opts, {
150 AST_APP_OPTION('q', OPTION_QUIET),
151 AST_APP_OPTION('b', OPTION_BRIDGED),
152 AST_APP_OPTION('w', OPTION_WHISPER),
153 AST_APP_OPTION('W', OPTION_PRIVATE),
154 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
155 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
156 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
157 AST_APP_OPTION('o', OPTION_READONLY),
158 AST_APP_OPTION('X', OPTION_EXIT),
162 struct chanspy_translation_helper {
164 struct ast_audiohook spy_audiohook;
165 struct ast_audiohook whisper_audiohook;
170 static void *spy_alloc(struct ast_channel *chan, void *data)
172 /* just store the data pointer in the channel structure */
176 static void spy_release(struct ast_channel *chan, void *data)
181 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
183 struct chanspy_translation_helper *csth = data;
184 struct ast_frame *f = NULL;
186 ast_audiohook_lock(&csth->spy_audiohook);
187 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
188 /* Channel is already gone more than likely */
189 ast_audiohook_unlock(&csth->spy_audiohook);
193 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
195 ast_audiohook_unlock(&csth->spy_audiohook);
200 if (ast_write(chan, f)) {
206 write(csth->fd, f->data, f->datalen);
213 static struct ast_generator spygen = {
215 .release = spy_release,
216 .generate = spy_generate,
219 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_audiohook *audiohook)
222 struct ast_channel *peer = NULL;
224 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
226 res = ast_audiohook_attach(chan, audiohook);
228 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
229 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
234 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
235 const struct ast_flags *flags, char *exitcontext)
237 struct chanspy_translation_helper csth;
238 int running = 0, res, x = 0;
242 struct ast_silence_generator *silgen = NULL;
244 if (ast_check_hangup(chan) || ast_check_hangup(spyee))
247 name = ast_strdupa(spyee->name);
248 ast_verb(2, "Spying on channel %s\n", name);
250 memset(&csth, 0, sizeof(csth));
252 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
254 if (start_spying(spyee, chan, &csth.spy_audiohook)) {
255 ast_audiohook_destroy(&csth.spy_audiohook);
259 if (ast_test_flag(flags, OPTION_WHISPER)) {
260 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
261 start_spying(spyee, chan, &csth.whisper_audiohook);
264 csth.volfactor = *volfactor;
266 if (csth.volfactor) {
267 csth.spy_audiohook.options.read_volume = csth.volfactor;
268 csth.spy_audiohook.options.write_volume = csth.volfactor;
273 if (ast_test_flag(flags, OPTION_PRIVATE))
274 silgen = ast_channel_start_silence_generator(chan);
276 ast_activate_generator(chan, &spygen, &csth);
278 /* We can no longer rely on 'spyee' being an actual channel;
279 it can be hung up and freed out from under us. However, the
280 channel destructor will put NULL into our csth.spy.chan
281 field when that happens, so that is our signal that the spyee
282 channel has gone away.
285 /* Note: it is very important that the ast_waitfor() be the first
286 condition in this expression, so that if we wait for some period
287 of time before receiving a frame from our spying channel, we check
288 for hangup on the spied-on channel _after_ knowing that a frame
289 has arrived, since the spied-on channel could have gone away while
292 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
293 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
298 if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
299 ast_audiohook_lock(&csth.whisper_audiohook);
300 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
301 ast_audiohook_unlock(&csth.whisper_audiohook);
306 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
311 if (x == sizeof(inp))
319 if (ast_test_flag(flags, OPTION_EXIT)) {
323 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
324 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
325 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
329 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
331 } else if (res >= '0' && res <= '9') {
338 } else if (res == '#') {
339 if (!ast_strlen_zero(inp)) {
347 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
349 csth.volfactor = *volfactor;
350 csth.spy_audiohook.options.read_volume = csth.volfactor;
351 csth.spy_audiohook.options.write_volume = csth.volfactor;
355 if (ast_test_flag(flags, OPTION_PRIVATE))
356 ast_channel_stop_silence_generator(chan, silgen);
358 ast_deactivate_generator(chan);
360 if (ast_test_flag(flags, OPTION_WHISPER)) {
361 ast_audiohook_lock(&csth.whisper_audiohook);
362 ast_audiohook_detach(&csth.whisper_audiohook);
363 ast_audiohook_unlock(&csth.whisper_audiohook);
364 ast_audiohook_destroy(&csth.whisper_audiohook);
367 ast_audiohook_lock(&csth.spy_audiohook);
368 ast_audiohook_detach(&csth.spy_audiohook);
369 ast_audiohook_unlock(&csth.spy_audiohook);
370 ast_audiohook_destroy(&csth.spy_audiohook);
372 ast_verb(2, "Done Spying on channel %s\n", name);
377 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
378 const char *exten, const char *context)
380 struct ast_channel *this;
384 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
386 this = ast_walk_channel_by_exten_locked(last, exten, context);
388 this = ast_channel_walk_locked(last);
391 ast_channel_unlock(this);
392 if (!strncmp(this->name, "Zap/pseudo", 10))
399 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
400 int volfactor, const int fd, const char *mygroup, const char *spec,
401 const char *exten, const char *context)
403 struct ast_channel *peer, *prev, *next;
404 char nameprefix[AST_NAME_STRLEN];
405 char peer_name[AST_NAME_STRLEN + 5];
406 char exitcontext[AST_MAX_CONTEXT] = "";
407 signed char zero_volume = 0;
412 int num_spyed_upon = 1;
414 if (ast_test_flag(flags, OPTION_EXIT)) {
416 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
417 ast_copy_string(exitcontext, c, sizeof(exitcontext));
418 else if (!ast_strlen_zero(chan->macrocontext))
419 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
421 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
424 if (chan->_state != AST_STATE_UP)
427 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
432 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
433 res = ast_streamfile(chan, "beep", chan->language);
435 res = ast_waitstream(chan, "");
437 ast_clear_flag(chan, AST_FLAG_SPYING);
440 if (!ast_strlen_zero(exitcontext)) {
444 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
447 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
451 res = ast_waitfordigit(chan, waitms);
453 ast_clear_flag(chan, AST_FLAG_SPYING);
456 if (!ast_strlen_zero(exitcontext)) {
460 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
463 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
466 /* reset for the next loop around, unless overridden later */
468 peer = prev = next = NULL;
471 for (peer = next_channel(peer, spec, exten, context);
473 prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
488 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
491 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
495 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
496 dup_group = ast_strdupa(group);
497 num_groups = ast_app_separate_args(dup_group, ':', groups,
498 sizeof(groups) / sizeof(groups[0]));
501 for (x = 0; x < num_groups; x++) {
502 if (!strcmp(mygroup, groups[x])) {
512 strcpy(peer_name, "spy-");
513 strncat(peer_name, peer->name, AST_NAME_STRLEN);
514 ptr = strchr(peer_name, '/');
517 for (s = peer_name; s < ptr; s++)
520 if (!ast_test_flag(flags, OPTION_QUIET)) {
521 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
522 res = ast_streamfile(chan, peer_name, chan->language);
524 res = ast_waitstream(chan, "");
528 res = ast_say_character_str(chan, peer_name, "", chan->language);
529 if ((num = atoi(ptr)))
530 ast_say_digits(chan, atoi(ptr), "", chan->language);
534 res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
539 } else if (res == -2) {
542 } else if (res > 1 && spec) {
543 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
544 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
545 ast_channel_unlock(next);
547 /* stay on this channel */
556 ast_clear_flag(chan, AST_FLAG_SPYING);
558 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
563 static int chanspy_exec(struct ast_channel *chan, void *data)
565 char *mygroup = NULL;
566 char *recbase = NULL;
568 struct ast_flags flags;
572 AST_DECLARE_APP_ARGS(args,
574 AST_APP_ARG(options);
576 char *opts[OPT_ARG_ARRAY_SIZE];
578 data = ast_strdupa(data);
579 AST_STANDARD_APP_ARGS(args, data);
581 if (args.spec && !strcmp(args.spec, "all"))
585 ast_app_parse_options(spy_opts, &flags, opts, args.options);
586 if (ast_test_flag(&flags, OPTION_GROUP))
587 mygroup = opts[OPT_ARG_GROUP];
589 if (ast_test_flag(&flags, OPTION_RECORD) &&
590 !(recbase = opts[OPT_ARG_RECORD]))
593 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
596 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
597 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
602 if (ast_test_flag(&flags, OPTION_PRIVATE))
603 ast_set_flag(&flags, OPTION_WHISPER);
605 ast_clear_flag(&flags, AST_FLAGS_ALL);
607 oldwf = chan->writeformat;
608 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
609 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
616 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
617 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
618 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
623 res = common_exec(chan, &flags, volfactor, fd, mygroup, args.spec, NULL, NULL);
628 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
629 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
634 static int extenspy_exec(struct ast_channel *chan, void *data)
636 char *ptr, *exten = NULL;
637 char *mygroup = NULL;
638 char *recbase = NULL;
640 struct ast_flags flags;
644 AST_DECLARE_APP_ARGS(args,
645 AST_APP_ARG(context);
646 AST_APP_ARG(options);
649 data = ast_strdupa(data);
651 AST_STANDARD_APP_ARGS(args, data);
652 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
653 exten = args.context;
658 if (ast_strlen_zero(args.context))
659 args.context = ast_strdupa(chan->context);
662 char *opts[OPT_ARG_ARRAY_SIZE];
664 ast_app_parse_options(spy_opts, &flags, opts, args.options);
665 if (ast_test_flag(&flags, OPTION_GROUP))
666 mygroup = opts[OPT_ARG_GROUP];
668 if (ast_test_flag(&flags, OPTION_RECORD) &&
669 !(recbase = opts[OPT_ARG_RECORD]))
672 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
675 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
676 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
681 if (ast_test_flag(&flags, OPTION_PRIVATE))
682 ast_set_flag(&flags, OPTION_WHISPER);
684 ast_clear_flag(&flags, AST_FLAGS_ALL);
686 oldwf = chan->writeformat;
687 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
688 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
695 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
696 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
697 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
702 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
707 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
708 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
713 static int unload_module(void)
717 res |= ast_unregister_application(app_chan);
718 res |= ast_unregister_application(app_ext);
723 static int load_module(void)
727 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
728 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
733 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");