2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2008, 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>
26 * \author Joshua Colp <jcolp@digium.com>
27 * \author Russell Bryant <russell@digium.com>
29 * \ingroup applications
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/audiohook.h"
42 #include "asterisk/features.h"
43 #include "asterisk/app.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/say.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/translate.h"
48 #include "asterisk/module.h"
49 #include "asterisk/lock.h"
51 #define AST_NAME_STRLEN 256
53 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
54 static const char *app_chan = "ChanSpy";
55 static const char *desc_chan =
56 " ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
57 "audio from an Asterisk channel. This includes the audio coming in and\n"
58 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
59 "only channels beginning with this string will be spied upon.\n"
60 " While spying, the following actions may be performed:\n"
61 " - Dialing # cycles the volume level.\n"
62 " - Dialing * will stop spying and look for another channel to spy on.\n"
63 " - Dialing a series of digits followed by # builds a channel name to append\n"
64 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
65 " the digits '1234#' while spying will begin spying on the channel\n"
67 " Note: The X option supersedes the three features above in that if a valid\n"
68 " single digit extension exists in the correct context ChanSpy will\n"
69 " exit to it. This also disables choosing a channel based on 'chanprefix'\n"
70 " and a digit sequence.\n"
72 " b - Only spy on channels involved in a bridged call.\n"
73 " g(grp) - Match only channels where their SPYGROUP variable is set to\n"
74 " contain 'grp' in an optional : delimited list.\n"
75 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
76 " selected channel name.\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 " s - Skip the playback of the channel type (i.e. SIP, IAX, etc) when\n"
81 " speaking the selected channel name.\n"
82 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
83 " negative value refers to a quieter setting.\n"
84 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
85 " the spied-on channel.\n"
86 " W - Enable 'private whisper' mode, so the spying channel can\n"
87 " talk to the spied-on channel but cannot listen to that\n"
89 " o - Only listen to audio coming from this channel.\n"
90 " X - Allow the user to exit ChanSpy to a valid single digit\n"
91 " numeric extension in the current context or the context\n"
92 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
93 " name of the last channel that was spied on will be stored\n"
94 " in the SPY_CHANNEL variable.\n"
95 " e(ext) - Enable 'enforced' mode, so the spying channel can\n"
96 " only monitor extensions whose name is in the 'ext' : \n"
100 static const char *app_ext = "ExtenSpy";
101 static const char *desc_ext =
102 " ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
103 "audio from an Asterisk channel. This includes the audio coming in and\n"
104 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
105 "specified extension will be selected for spying. If the optional context is not\n"
106 "supplied, the current channel's context will be used.\n"
107 " While spying, the following actions may be performed:\n"
108 " - Dialing # cycles the volume level.\n"
109 " - Dialing * will stop spying and look for another channel to spy on.\n"
110 " Note: The X option superseeds the two features above in that if a valid\n"
111 " single digit extension exists in the correct context it ChanSpy will\n"
114 " b - Only spy on channels involved in a bridged call.\n"
115 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
116 " contain 'grp' in an optional : delimited list.\n"
117 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
118 " selected channel name.\n"
119 " r[(basename)] - Record the session to the monitor spool directory. An\n"
120 " optional base for the filename may be specified. The\n"
121 " default is 'chanspy'.\n"
122 " s - Skip the playback of the channel type (i.e. SIP, IAX, etc) when\n"
123 " speaking the selected channel name.\n"
124 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
125 " negative value refers to a quieter setting.\n"
126 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
127 " the spied-on channel.\n"
128 " W - Enable 'private whisper' mode, so the spying channel can\n"
129 " talk to the spied-on channel but cannot listen to that\n"
131 " o - Only listen to audio coming from this channel.\n"
132 " X - Allow the user to exit ChanSpy to a valid single digit\n"
133 " numeric extension in the current context or the context\n"
134 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
135 " name of the last channel that was spied on will be stored\n"
136 " in the SPY_CHANNEL variable.\n"
140 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
141 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
142 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
143 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
144 OPTION_RECORD = (1 << 4),
145 OPTION_WHISPER = (1 << 5),
146 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
147 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
148 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
149 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
150 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
161 AST_APP_OPTIONS(spy_opts, {
162 AST_APP_OPTION('q', OPTION_QUIET),
163 AST_APP_OPTION('b', OPTION_BRIDGED),
164 AST_APP_OPTION('w', OPTION_WHISPER),
165 AST_APP_OPTION('W', OPTION_PRIVATE),
166 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
167 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
168 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
169 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
170 AST_APP_OPTION('o', OPTION_READONLY),
171 AST_APP_OPTION('X', OPTION_EXIT),
172 AST_APP_OPTION('s', OPTION_NOTECH),
176 struct chanspy_translation_helper {
178 struct ast_audiohook spy_audiohook;
179 struct ast_audiohook whisper_audiohook;
184 static void *spy_alloc(struct ast_channel *chan, void *data)
186 /* just store the data pointer in the channel structure */
190 static void spy_release(struct ast_channel *chan, void *data)
195 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
197 struct chanspy_translation_helper *csth = data;
198 struct ast_frame *f = NULL;
200 ast_audiohook_lock(&csth->spy_audiohook);
201 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
202 /* Channel is already gone more than likely */
203 ast_audiohook_unlock(&csth->spy_audiohook);
207 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
209 ast_audiohook_unlock(&csth->spy_audiohook);
214 if (ast_write(chan, f)) {
220 write(csth->fd, f->data, f->datalen);
227 static struct ast_generator spygen = {
229 .release = spy_release,
230 .generate = spy_generate,
233 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
236 struct ast_channel *peer = NULL;
238 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
240 res = ast_audiohook_attach(chan, audiohook);
242 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
243 ast_channel_unlock(chan);
244 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
246 ast_channel_unlock(chan);
252 struct ast_channel *chan;
256 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
257 int *volfactor, int fd, const struct ast_flags *flags, char *exitcontext)
259 struct chanspy_translation_helper csth;
260 int running = 0, res, x = 0;
264 struct ast_silence_generator *silgen = NULL;
265 struct ast_channel *spyee = NULL;
266 const char *spyer_name;
268 ast_channel_lock(chan);
269 spyer_name = ast_strdupa(chan->name);
270 ast_channel_unlock(chan);
272 ast_mutex_lock(&spyee_chanspy_ds->lock);
273 if (spyee_chanspy_ds->chan) {
274 spyee = spyee_chanspy_ds->chan;
275 ast_channel_lock(spyee);
277 ast_mutex_unlock(&spyee_chanspy_ds->lock);
282 /* We now hold the channel lock on spyee */
284 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
285 ast_channel_unlock(spyee);
289 name = ast_strdupa(spyee->name);
290 ast_verb(2, "Spying on channel %s\n", name);
292 memset(&csth, 0, sizeof(csth));
294 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
296 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { /* Unlocks spyee */
297 ast_audiohook_destroy(&csth.spy_audiohook);
301 if (ast_test_flag(flags, OPTION_WHISPER)) {
302 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
303 start_spying(spyee, spyer_name, &csth.whisper_audiohook); /* Unlocks spyee */
308 csth.volfactor = *volfactor;
310 if (csth.volfactor) {
311 csth.spy_audiohook.options.read_volume = csth.volfactor;
312 csth.spy_audiohook.options.write_volume = csth.volfactor;
317 if (ast_test_flag(flags, OPTION_PRIVATE))
318 silgen = ast_channel_start_silence_generator(chan);
320 ast_activate_generator(chan, &spygen, &csth);
322 /* We can no longer rely on 'spyee' being an actual channel;
323 it can be hung up and freed out from under us. However, the
324 channel destructor will put NULL into our csth.spy.chan
325 field when that happens, so that is our signal that the spyee
326 channel has gone away.
329 /* Note: it is very important that the ast_waitfor() be the first
330 condition in this expression, so that if we wait for some period
331 of time before receiving a frame from our spying channel, we check
332 for hangup on the spied-on channel _after_ knowing that a frame
333 has arrived, since the spied-on channel could have gone away while
336 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
337 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
342 if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
343 ast_audiohook_lock(&csth.whisper_audiohook);
344 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
345 ast_audiohook_unlock(&csth.whisper_audiohook);
350 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
355 if (x == sizeof(inp))
363 if (ast_test_flag(flags, OPTION_EXIT)) {
367 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
368 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
369 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
373 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
375 } else if (res >= '0' && res <= '9') {
382 } else if (res == '#') {
383 if (!ast_strlen_zero(inp)) {
391 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
393 csth.volfactor = *volfactor;
394 csth.spy_audiohook.options.read_volume = csth.volfactor;
395 csth.spy_audiohook.options.write_volume = csth.volfactor;
399 if (ast_test_flag(flags, OPTION_PRIVATE))
400 ast_channel_stop_silence_generator(chan, silgen);
402 ast_deactivate_generator(chan);
404 if (ast_test_flag(flags, OPTION_WHISPER)) {
405 ast_audiohook_lock(&csth.whisper_audiohook);
406 ast_audiohook_detach(&csth.whisper_audiohook);
407 ast_audiohook_unlock(&csth.whisper_audiohook);
408 ast_audiohook_destroy(&csth.whisper_audiohook);
411 ast_audiohook_lock(&csth.spy_audiohook);
412 ast_audiohook_detach(&csth.spy_audiohook);
413 ast_audiohook_unlock(&csth.spy_audiohook);
414 ast_audiohook_destroy(&csth.spy_audiohook);
416 ast_verb(2, "Done Spying on channel %s\n", name);
422 * \note This relies on the embedded lock to be recursive, as it may be called
423 * due to a call to chanspy_ds_free with the lock held there.
425 static void chanspy_ds_destroy(void *data)
427 struct chanspy_ds *chanspy_ds = data;
429 /* Setting chan to be NULL is an atomic operation, but we don't want this
430 * value to change while this lock is held. The lock is held elsewhere
431 * while it performs non-atomic operations with this channel pointer */
433 ast_mutex_lock(&chanspy_ds->lock);
434 chanspy_ds->chan = NULL;
435 ast_mutex_unlock(&chanspy_ds->lock);
438 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
440 struct chanspy_ds *chanspy_ds = data;
442 ast_mutex_lock(&chanspy_ds->lock);
443 chanspy_ds->chan = new_chan;
444 ast_mutex_unlock(&chanspy_ds->lock);
447 static const struct ast_datastore_info chanspy_ds_info = {
449 .destroy = chanspy_ds_destroy,
450 .chan_fixup = chanspy_ds_chan_fixup,
453 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
458 ast_mutex_lock(&chanspy_ds->lock);
459 if (chanspy_ds->chan) {
460 struct ast_datastore *datastore;
461 struct ast_channel *chan;
463 chan = chanspy_ds->chan;
465 ast_channel_lock(chan);
466 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, NULL))) {
467 ast_channel_datastore_remove(chan, datastore);
468 /* chanspy_ds->chan is NULL after this call */
469 chanspy_ds_destroy(datastore->data);
470 datastore->data = NULL;
471 ast_channel_datastore_free(datastore);
473 ast_channel_unlock(chan);
475 ast_mutex_unlock(&chanspy_ds->lock);
480 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
481 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
483 struct ast_datastore *datastore = NULL;
485 ast_mutex_lock(&chanspy_ds->lock);
487 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, NULL))) {
488 ast_mutex_unlock(&chanspy_ds->lock);
489 chanspy_ds = chanspy_ds_free(chanspy_ds);
490 ast_channel_unlock(chan);
494 chanspy_ds->chan = chan;
495 datastore->data = chanspy_ds;
496 ast_channel_datastore_add(chan, datastore);
501 static struct chanspy_ds *next_channel(struct ast_channel *chan,
502 const struct ast_channel *last, const char *spec,
503 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
505 struct ast_channel *next;
508 if (!ast_strlen_zero(spec))
509 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
510 else if (!ast_strlen_zero(exten))
511 next = ast_walk_channel_by_exten_locked(last, exten, context);
513 next = ast_channel_walk_locked(last);
518 if (!strncmp(next->name, "Zap/pseudo", 10)) {
519 ast_channel_unlock(next);
521 } else if (next == chan) {
523 ast_channel_unlock(next);
527 return setup_chanspy_ds(next, chanspy_ds);
530 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
531 int volfactor, const int fd, const char *mygroup, const char *myenforced,
532 const char *spec, const char *exten, const char *context)
534 char nameprefix[AST_NAME_STRLEN];
535 char peer_name[AST_NAME_STRLEN + 5];
536 char exitcontext[AST_MAX_CONTEXT] = "";
537 signed char zero_volume = 0;
542 int num_spyed_upon = 1;
543 struct chanspy_ds chanspy_ds;
545 if (ast_test_flag(flags, OPTION_EXIT)) {
547 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
548 ast_copy_string(exitcontext, c, sizeof(exitcontext));
549 else if (!ast_strlen_zero(chan->macrocontext))
550 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
552 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
555 ast_mutex_init(&chanspy_ds.lock);
557 if (chan->_state != AST_STATE_UP)
560 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
565 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
566 struct ast_channel *prev = NULL, *peer = NULL;
568 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
569 res = ast_streamfile(chan, "beep", chan->language);
571 res = ast_waitstream(chan, "");
573 ast_clear_flag(chan, AST_FLAG_SPYING);
576 if (!ast_strlen_zero(exitcontext)) {
580 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
583 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
587 res = ast_waitfordigit(chan, waitms);
589 ast_clear_flag(chan, AST_FLAG_SPYING);
592 if (!ast_strlen_zero(exitcontext)) {
596 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
599 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
602 /* reset for the next loop around, unless overridden later */
606 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
608 chanspy_ds_free(peer_chanspy_ds), prev = peer,
609 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
610 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
622 int ienf = !myenforced;
623 struct ast_channel *peer;
625 peer = peer_chanspy_ds->chan;
627 ast_mutex_unlock(&peer_chanspy_ds->lock);
630 ast_channel_unlock(peer);
631 chanspy_ds_free(peer_chanspy_ds);
635 if (ast_check_hangup(chan)) {
636 ast_channel_unlock(peer);
637 chanspy_ds_free(peer_chanspy_ds);
641 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
642 ast_channel_unlock(peer);
646 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
647 ast_channel_unlock(peer);
652 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
653 dup_group = ast_strdupa(group);
654 num_groups = ast_app_separate_args(dup_group, ':', groups,
655 sizeof(groups) / sizeof(groups[0]));
658 for (x = 0; x < num_groups; x++) {
659 if (!strcmp(mygroup, groups[x])) {
667 ast_channel_unlock(peer);
673 /* We don't need to allocate more space than just the
674 length of (peer->name) for ext as we will cut the
675 channel name's ending before copying into ext */
677 ext = alloca(strlen(peer->name));
679 form_enforced = alloca(strlen(myenforced) + 3);
681 strcpy(form_enforced, ":");
682 strcat(form_enforced, myenforced);
683 strcat(form_enforced, ":");
685 buffer = ast_strdupa(peer->name);
687 if ((end = strchr(buffer, '-'))) {
695 if (strcasestr(form_enforced, ext))
702 strcpy(peer_name, "spy-");
703 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
704 ptr = strchr(peer_name, '/');
707 for (s = peer_name; s < ptr; s++)
710 /* We have to unlock the peer channel here to avoid a deadlock.
711 * So, when we need it again, we have to lock the datastore and get
712 * the pointer from there to see if the channel is still valid. */
713 ast_channel_unlock(peer);
716 if (!ast_test_flag(flags, OPTION_QUIET)) {
717 if (!ast_test_flag(flags, OPTION_NOTECH)) {
718 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
719 res = ast_streamfile(chan, peer_name, chan->language);
721 res = ast_waitstream(chan, "");
724 chanspy_ds_free(peer_chanspy_ds);
728 res = ast_say_character_str(chan, peer_name, "", chan->language);
731 if ((num = atoi(ptr)))
732 ast_say_digits(chan, atoi(ptr), "", chan->language);
736 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
740 chanspy_ds_free(peer_chanspy_ds);
742 } else if (res == -2) {
744 chanspy_ds_free(peer_chanspy_ds);
746 } else if (res > 1 && spec) {
747 struct ast_channel *next;
749 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
751 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
752 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
753 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
755 /* stay on this channel, if it is still valid */
757 ast_mutex_lock(&peer_chanspy_ds->lock);
758 if (peer_chanspy_ds->chan) {
759 ast_channel_lock(peer_chanspy_ds->chan);
760 next_chanspy_ds = peer_chanspy_ds;
761 peer_chanspy_ds = NULL;
763 /* the channel is gone */
764 ast_mutex_unlock(&peer_chanspy_ds->lock);
765 next_chanspy_ds = NULL;
772 if (res == -1 || ast_check_hangup(chan))
777 ast_clear_flag(chan, AST_FLAG_SPYING);
779 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
781 ast_mutex_destroy(&chanspy_ds.lock);
786 static int chanspy_exec(struct ast_channel *chan, void *data)
788 char *myenforced = NULL;
789 char *mygroup = NULL;
790 char *recbase = NULL;
792 struct ast_flags flags;
796 AST_DECLARE_APP_ARGS(args,
798 AST_APP_ARG(options);
800 char *opts[OPT_ARG_ARRAY_SIZE];
802 data = ast_strdupa(data);
803 AST_STANDARD_APP_ARGS(args, data);
805 if (args.spec && !strcmp(args.spec, "all"))
809 ast_app_parse_options(spy_opts, &flags, opts, args.options);
810 if (ast_test_flag(&flags, OPTION_GROUP))
811 mygroup = opts[OPT_ARG_GROUP];
813 if (ast_test_flag(&flags, OPTION_RECORD) &&
814 !(recbase = opts[OPT_ARG_RECORD]))
817 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
820 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
821 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
826 if (ast_test_flag(&flags, OPTION_PRIVATE))
827 ast_set_flag(&flags, OPTION_WHISPER);
829 if (ast_test_flag(&flags, OPTION_ENFORCED))
830 myenforced = opts[OPT_ARG_ENFORCED];
833 ast_clear_flag(&flags, AST_FLAGS_ALL);
835 oldwf = chan->writeformat;
836 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
837 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
844 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
845 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
846 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
851 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL);
856 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
857 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
862 static int extenspy_exec(struct ast_channel *chan, void *data)
864 char *ptr, *exten = NULL;
865 char *mygroup = NULL;
866 char *recbase = NULL;
868 struct ast_flags flags;
872 AST_DECLARE_APP_ARGS(args,
873 AST_APP_ARG(context);
874 AST_APP_ARG(options);
877 data = ast_strdupa(data);
879 AST_STANDARD_APP_ARGS(args, data);
880 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
881 exten = args.context;
886 if (ast_strlen_zero(args.context))
887 args.context = ast_strdupa(chan->context);
890 char *opts[OPT_ARG_ARRAY_SIZE];
892 ast_app_parse_options(spy_opts, &flags, opts, args.options);
893 if (ast_test_flag(&flags, OPTION_GROUP))
894 mygroup = opts[OPT_ARG_GROUP];
896 if (ast_test_flag(&flags, OPTION_RECORD) &&
897 !(recbase = opts[OPT_ARG_RECORD]))
900 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
903 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
904 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
909 if (ast_test_flag(&flags, OPTION_PRIVATE))
910 ast_set_flag(&flags, OPTION_WHISPER);
912 ast_clear_flag(&flags, AST_FLAGS_ALL);
914 oldwf = chan->writeformat;
915 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
916 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
923 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
924 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
925 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
931 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context);
936 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
937 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
942 static int unload_module(void)
946 res |= ast_unregister_application(app_chan);
947 res |= ast_unregister_application(app_ext);
952 static int load_module(void)
956 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
957 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
962 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");