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
33 <support_level>core</support_level>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
44 #include "asterisk/file.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/audiohook.h"
47 #include "asterisk/features.h"
48 #include "asterisk/app.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/say.h"
51 #include "asterisk/pbx.h"
52 #include "asterisk/translate.h"
53 #include "asterisk/manager.h"
54 #include "asterisk/module.h"
55 #include "asterisk/lock.h"
56 #include "asterisk/options.h"
57 #include "asterisk/autochan.h"
59 #define AST_NAME_STRLEN 256
60 #define NUM_SPYGROUPS 128
63 <application name="ChanSpy" language="en_US">
65 Listen to a channel, and optionally whisper into it.
68 <parameter name="chanprefix" />
69 <parameter name="options">
72 <para>Only spy on channels involved in a bridged call.</para>
75 <para>Instead of whispering on a single channel barge in on both
76 channels involved in the call.</para>
79 <argument name="digit" required="true">
80 <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
84 <para>Override the typical numeric DTMF functionality and instead
85 use DTMF to switch between spy modes.</para>
91 <para>whisper mode</para>
94 <para>barge mode</para>
99 <argument name="ext" required="true" />
100 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
101 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
105 <para>Exit when the spied-on channel hangs up.</para>
108 <argument name="grp" required="true">
109 <para>Only spy on channels in which one or more of the groups
110 listed in <replaceable>grp</replaceable> matches one or more groups from the
111 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
113 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
114 either a single group or a colon-delimited list of groups, such
115 as <literal>sales:support:accounting</literal>.</para></note>
117 <option name="n" argsep="@">
118 <para>Say the name of the person being spied on if that person has recorded
119 his/her name. If a context is specified, then that voicemail context will
120 be searched when retrieving the name, otherwise the <literal>default</literal> context
121 be used when searching for the name (i.e. if SIP/1000 is the channel being
122 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
123 for the name).</para>
124 <argument name="mailbox" />
125 <argument name="context" />
128 <para>Only listen to audio coming from this channel.</para>
131 <para>Don't play a beep when beginning to spy on a channel, or speak the
132 selected channel name.</para>
135 <para>Record the session to the monitor spool directory. An optional base for the filename
136 may be specified. The default is <literal>chanspy</literal>.</para>
137 <argument name="basename" />
140 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
141 speaking the selected channel name.</para>
144 <para>Stop when no more channels are left to spy on.</para>
147 <argument name="value" />
148 <para>Adjust the initial volume in the range from <literal>-4</literal>
149 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
152 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
153 the spied-on channel.</para>
156 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
157 talk to the spied-on channel but cannot listen to that channel.</para>
160 <argument name="digit" required="true">
161 <para>Specify a DTMF digit that can be used to exit the application.</para>
165 <para>Allow the user to exit ChanSpy to a valid single digit
166 numeric extension in the current context or the context
167 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
168 name of the last channel that was spied on will be stored
169 in the <variable>SPY_CHANNEL</variable> variable.</para>
175 <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio
176 coming in and out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
177 only channels beginning with this string will be spied upon.</para>
178 <para>While spying, the following actions may be performed:</para>
179 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
180 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
181 <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
182 to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#'
183 while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden if the 'd' option
185 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
186 single digit extension exists in the correct context ChanSpy will exit to it.
187 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
190 <ref type="application">ExtenSpy</ref>
193 <application name="ExtenSpy" language="en_US">
195 Listen to a channel, and optionally whisper into it.
198 <parameter name="exten" required="true" argsep="@">
199 <argument name="exten" required="true">
200 <para>Specify extension.</para>
202 <argument name="context">
203 <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
206 <parameter name="options">
209 <para>Only spy on channels involved in a bridged call.</para>
212 <para>Instead of whispering on a single channel barge in on both
213 channels involved in the call.</para>
216 <argument name="digit" required="true">
217 <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
221 <para>Override the typical numeric DTMF functionality and instead
222 use DTMF to switch between spy modes.</para>
225 <para>spy mode</para>
228 <para>whisper mode</para>
231 <para>barge mode</para>
236 <argument name="ext" required="true" />
237 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
238 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
242 <para>Exit when the spied-on channel hangs up.</para>
245 <argument name="grp" required="true">
246 <para>Only spy on channels in which one or more of the groups
247 listed in <replaceable>grp</replaceable> matches one or more groups from the
248 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
250 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
251 either a single group or a colon-delimited list of groups, such
252 as <literal>sales:support:accounting</literal>.</para></note>
254 <option name="n" argsep="@">
255 <para>Say the name of the person being spied on if that person has recorded
256 his/her name. If a context is specified, then that voicemail context will
257 be searched when retrieving the name, otherwise the <literal>default</literal> context
258 be used when searching for the name (i.e. if SIP/1000 is the channel being
259 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
260 for the name).</para>
261 <argument name="mailbox" />
262 <argument name="context" />
265 <para>Only listen to audio coming from this channel.</para>
268 <para>Don't play a beep when beginning to spy on a channel, or speak the
269 selected channel name.</para>
272 <para>Record the session to the monitor spool directory. An optional base for the filename
273 may be specified. The default is <literal>chanspy</literal>.</para>
274 <argument name="basename" />
277 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
278 speaking the selected channel name.</para>
281 <para>Stop when there are no more extensions left to spy on.</para>
284 <argument name="value" />
285 <para>Adjust the initial volume in the range from <literal>-4</literal>
286 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
289 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
290 the spied-on channel.</para>
293 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
294 talk to the spied-on channel but cannot listen to that channel.</para>
297 <argument name="digit" required="true">
298 <para>Specify a DTMF digit that can be used to exit the application.</para>
302 <para>Allow the user to exit ChanSpy to a valid single digit
303 numeric extension in the current context or the context
304 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
305 name of the last channel that was spied on will be stored
306 in the <variable>SPY_CHANNEL</variable> variable.</para>
312 <para>This application is used to listen to the audio from an Asterisk channel. This includes
313 the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
314 specified extension will be selected for spying. If the optional context is not supplied,
315 the current channel's context will be used.</para>
316 <para>While spying, the following actions may be performed:</para>
317 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
318 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
319 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
320 single digit extension exists in the correct context ChanSpy will exit to it.
321 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
324 <ref type="application">ChanSpy</ref>
328 <application name="DAHDIScan" language="en_US">
330 Scan DAHDI channels to monitor calls.
333 <parameter name="group">
334 <para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
338 <para>Allows a call center manager to monitor DAHDI channels in a
339 convenient way. Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
344 static const char app_chan[] = "ChanSpy";
346 static const char app_ext[] = "ExtenSpy";
348 static const char app_dahdiscan[] = "DAHDIScan";
351 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
352 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
353 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
354 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
355 OPTION_RECORD = (1 << 4),
356 OPTION_WHISPER = (1 << 5),
357 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
358 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
359 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
360 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
361 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
362 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
363 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
364 OPTION_DTMF_SWITCH_MODES = (1 << 13), /* Allow numeric DTMF to switch between chanspy modes */
365 OPTION_DTMF_EXIT = (1 << 14), /* Set DTMF to exit, added for DAHDIScan integration */
366 OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next avaliable channel, (default is '*') */
367 OPTION_DAHDI_SCAN = (1 << 16), /* Scan groups in DAHDIScan mode */
368 OPTION_STOP = (1 << 17),
369 OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */
383 AST_APP_OPTIONS(spy_opts, {
384 AST_APP_OPTION('b', OPTION_BRIDGED),
385 AST_APP_OPTION('B', OPTION_BARGE),
386 AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
387 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
388 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
389 AST_APP_OPTION('E', OPTION_EXITONHANGUP),
390 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
391 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
392 AST_APP_OPTION('o', OPTION_READONLY),
393 AST_APP_OPTION('q', OPTION_QUIET),
394 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
395 AST_APP_OPTION('s', OPTION_NOTECH),
396 AST_APP_OPTION('S', OPTION_STOP),
397 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
398 AST_APP_OPTION('w', OPTION_WHISPER),
399 AST_APP_OPTION('W', OPTION_PRIVATE),
400 AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT),
401 AST_APP_OPTION('X', OPTION_EXIT),
404 struct chanspy_translation_helper {
406 struct ast_audiohook spy_audiohook;
407 struct ast_audiohook whisper_audiohook;
408 struct ast_audiohook bridge_whisper_audiohook;
411 struct ast_flags flags;
414 struct spy_dtmf_options {
420 static void *spy_alloc(struct ast_channel *chan, void *data)
422 /* just store the data pointer in the channel structure */
426 static void spy_release(struct ast_channel *chan, void *data)
431 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
433 struct chanspy_translation_helper *csth = data;
434 struct ast_frame *f, *cur;
435 struct ast_format format_slin;
437 ast_format_set(&format_slin, AST_FORMAT_SLINEAR, 0);
439 ast_audiohook_lock(&csth->spy_audiohook);
440 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
441 /* Channel is already gone more than likely */
442 ast_audiohook_unlock(&csth->spy_audiohook);
446 if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
447 /* Option 'o' was set, so don't mix channel audio */
448 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, &format_slin);
450 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, &format_slin);
453 ast_audiohook_unlock(&csth->spy_audiohook);
458 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
459 if (ast_write(chan, cur)) {
465 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
466 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
476 static struct ast_generator spygen = {
478 .release = spy_release,
479 .generate = spy_generate,
482 static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
485 struct ast_channel *peer = NULL;
487 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
489 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
490 res = ast_audiohook_attach(autochan->chan, audiohook);
492 if (!res && ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
493 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
498 static void change_spy_mode(const char digit, struct ast_flags *flags)
501 ast_clear_flag(flags, OPTION_WHISPER);
502 ast_clear_flag(flags, OPTION_BARGE);
503 } else if (digit == '5') {
504 ast_clear_flag(flags, OPTION_BARGE);
505 ast_set_flag(flags, OPTION_WHISPER);
506 } else if (digit == '6') {
507 ast_clear_flag(flags, OPTION_WHISPER);
508 ast_set_flag(flags, OPTION_BARGE);
512 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
513 int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
516 struct chanspy_translation_helper csth;
517 int running = 0, res, x = 0;
521 struct ast_silence_generator *silgen = NULL;
522 struct ast_autochan *spyee_bridge_autochan = NULL;
523 const char *spyer_name;
524 struct ast_channel *chans[] = { chan, spyee_autochan->chan };
526 ast_channel_lock(chan);
527 spyer_name = ast_strdupa(ast_channel_name(chan));
528 ast_channel_unlock(chan);
530 /* We now hold the channel lock on spyee */
532 if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) ||
533 ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) {
537 ast_channel_lock(spyee_autochan->chan);
538 name = ast_strdupa(ast_channel_name(spyee_autochan->chan));
539 ast_channel_unlock(spyee_autochan->chan);
541 ast_verb(2, "Spying on channel %s\n", name);
543 <managerEventInstance>
544 <synopsis>Raised when a channel has started spying on another channel.</synopsis>
546 <ref type="application">ChanSpy</ref>
547 <ref type="application">ExtenSpy</ref>
548 <ref type="managerEvent">ChanSpyStop</ref>
550 </managerEventInstance>
552 ast_manager_event_multichan(EVENT_FLAG_CALL, "ChanSpyStart", 2, chans,
553 "SpyerChannel: %s\r\n"
554 "SpyeeChannel: %s\r\n",
557 memset(&csth, 0, sizeof(csth));
558 ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
560 /* This is the audiohook which gives us the audio off the channel we are
563 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0);
565 if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
566 ast_audiohook_destroy(&csth.spy_audiohook);
570 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
571 /* This audiohook will let us inject audio from our channel into the
572 channel we are currently spying on.
574 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0);
576 if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
577 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
581 if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
582 /* And this hook lets us inject audio into the channel that the spied on
583 channel is currently bridged with.
585 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0);
587 if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
588 ast_channel_lock(spyee_bridge_autochan->chan);
589 if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
590 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
592 ast_channel_unlock(spyee_bridge_autochan->chan);
596 ast_channel_lock(chan);
597 ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
598 ast_channel_unlock(chan);
600 csth.volfactor = *volfactor;
602 if (csth.volfactor) {
603 csth.spy_audiohook.options.read_volume = csth.volfactor;
604 csth.spy_audiohook.options.write_volume = csth.volfactor;
609 if (ast_test_flag(flags, OPTION_PRIVATE))
610 silgen = ast_channel_start_silence_generator(chan);
612 ast_activate_generator(chan, &spygen, &csth);
614 /* We can no longer rely on 'spyee' being an actual channel;
615 it can be hung up and freed out from under us. However, the
616 channel destructor will put NULL into our csth.spy.chan
617 field when that happens, so that is our signal that the spyee
618 channel has gone away.
621 /* Note: it is very important that the ast_waitfor() be the first
622 condition in this expression, so that if we wait for some period
623 of time before receiving a frame from our spying channel, we check
624 for hangup on the spied-on channel _after_ knowing that a frame
625 has arrived, since the spied-on channel could have gone away while
628 while (ast_waitfor(chan, -1) > -1 && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
629 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
634 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
635 ast_audiohook_lock(&csth.whisper_audiohook);
636 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
637 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
638 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
639 ast_audiohook_unlock(&csth.whisper_audiohook);
640 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
643 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
644 ast_audiohook_lock(&csth.whisper_audiohook);
645 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
646 ast_audiohook_unlock(&csth.whisper_audiohook);
651 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0;
656 if (x == sizeof(inp))
664 if (ast_test_flag(flags, OPTION_EXIT)) {
668 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
669 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
670 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
674 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
676 } else if (res >= '0' && res <= '9') {
677 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
678 change_spy_mode(res, flags);
684 if (res == user_options->cycle) {
687 } else if (res == user_options->exit) {
690 } else if (res == user_options->volume) {
691 if (!ast_strlen_zero(inp)) {
699 ast_verb(3, "Setting spy volume on %s to %d\n", ast_channel_name(chan), *volfactor);
701 csth.volfactor = *volfactor;
702 csth.spy_audiohook.options.read_volume = csth.volfactor;
703 csth.spy_audiohook.options.write_volume = csth.volfactor;
707 if (ast_test_flag(flags, OPTION_PRIVATE))
708 ast_channel_stop_silence_generator(chan, silgen);
710 ast_deactivate_generator(chan);
712 ast_channel_lock(chan);
713 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
714 ast_channel_unlock(chan);
716 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
717 ast_audiohook_lock(&csth.whisper_audiohook);
718 ast_audiohook_detach(&csth.whisper_audiohook);
719 ast_audiohook_unlock(&csth.whisper_audiohook);
720 ast_audiohook_destroy(&csth.whisper_audiohook);
723 if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
724 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
725 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
726 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
727 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
730 ast_audiohook_lock(&csth.spy_audiohook);
731 ast_audiohook_detach(&csth.spy_audiohook);
732 ast_audiohook_unlock(&csth.spy_audiohook);
733 ast_audiohook_destroy(&csth.spy_audiohook);
735 if (spyee_bridge_autochan) {
736 ast_autochan_destroy(spyee_bridge_autochan);
739 ast_verb(2, "Done Spying on channel %s\n", name);
741 <managerEventInstance>
742 <synopsis>Raised when a channel has stopped spying on another channel.</synopsis>
744 <ref type="managerEvent">ChanSpyStart</ref>
746 </managerEventInstance>
748 ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
753 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
754 struct ast_autochan *autochan, struct ast_channel *chan)
756 struct ast_channel *next;
757 struct ast_autochan *autochan_store;
758 const size_t pseudo_len = strlen("DAHDI/pseudo");
764 for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) {
765 if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len)
770 autochan_store = ast_autochan_setup(next);
771 ast_channel_unref(next);
773 return autochan_store;
778 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
779 int volfactor, const int fd, struct spy_dtmf_options *user_options,
780 const char *mygroup, const char *myenforced, const char *spec, const char *exten,
781 const char *context, const char *mailbox, const char *name_context)
783 char nameprefix[AST_NAME_STRLEN];
784 char exitcontext[AST_MAX_CONTEXT] = "";
785 signed char zero_volume = 0;
788 int num_spyed_upon = 1;
789 struct ast_channel_iterator *iter = NULL;
791 if (ast_test_flag(flags, OPTION_EXIT)) {
793 ast_channel_lock(chan);
794 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
795 ast_copy_string(exitcontext, c, sizeof(exitcontext));
796 } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
797 ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
799 ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
801 ast_channel_unlock(chan);
804 if (ast_channel_state(chan) != AST_STATE_UP)
807 ast_set_flag(ast_channel_flags(chan), AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
812 struct ast_autochan *autochan = NULL, *next_autochan = NULL;
813 struct ast_channel *prev = NULL;
815 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
816 res = ast_streamfile(chan, "beep", ast_channel_language(chan));
818 res = ast_waitstream(chan, "");
820 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
823 if (!ast_strlen_zero(exitcontext)) {
827 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
830 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
834 /* Set up the iterator we'll be using during this call */
835 if (!ast_strlen_zero(spec)) {
836 iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
837 } else if (!ast_strlen_zero(exten)) {
838 iter = ast_channel_iterator_by_exten_new(exten, context);
840 iter = ast_channel_iterator_all_new();
848 res = ast_waitfordigit(chan, waitms);
850 iter = ast_channel_iterator_destroy(iter);
851 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
854 if (!ast_strlen_zero(exitcontext)) {
858 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
859 iter = ast_channel_iterator_destroy(iter);
862 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
866 /* reset for the next loop around, unless overridden later */
870 for (autochan = next_channel(iter, autochan, chan);
872 prev = autochan->chan, ast_autochan_destroy(autochan),
873 autochan = next_autochan ? next_autochan :
874 next_channel(iter, autochan, chan), next_autochan = NULL) {
876 int ienf = !myenforced;
878 if (autochan->chan == prev) {
879 ast_autochan_destroy(autochan);
883 if (ast_check_hangup(chan)) {
884 ast_autochan_destroy(autochan);
888 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
892 if (ast_check_hangup(autochan->chan) || ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_SPYING)) {
898 int num_mygroups = 0;
900 char dup_mygroup[512];
901 char *groups[NUM_SPYGROUPS];
902 char *mygroups[NUM_SPYGROUPS];
903 const char *group = NULL;
906 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
907 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
908 ARRAY_LEN(mygroups));
910 /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable
911 * rather than "SPYGROUP", this check is done to preserve expected behavior */
912 if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
913 group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
915 group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
918 if (!ast_strlen_zero(group)) {
919 ast_copy_string(dup_group, group, sizeof(dup_group));
920 num_groups = ast_app_separate_args(dup_group, ':', groups,
924 for (y = 0; y < num_mygroups; y++) {
925 for (x = 0; x < num_groups; x++) {
926 if (!strcmp(mygroups[y], groups[x])) {
938 char ext[AST_CHANNEL_NAME + 3];
942 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
944 ast_copy_string(ext + 1, ast_channel_name(autochan->chan), sizeof(ext) - 1);
945 if ((end = strchr(ext, '-'))) {
952 if (strcasestr(buffer, ext)) {
961 if (!ast_test_flag(flags, OPTION_QUIET)) {
962 char peer_name[AST_NAME_STRLEN + 5];
965 strcpy(peer_name, "spy-");
966 strncat(peer_name, ast_channel_name(autochan->chan), AST_NAME_STRLEN - 4 - 1);
967 if ((ptr = strchr(peer_name, '/'))) {
969 for (s = peer_name; s < ptr; s++) {
972 if ((s = strchr(ptr, '-'))) {
977 if (ast_test_flag(flags, OPTION_NAME)) {
978 const char *local_context = S_OR(name_context, "default");
979 const char *local_mailbox = S_OR(mailbox, ptr);
981 res = ast_app_sayname(chan, local_mailbox, local_context);
986 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
988 if (!ast_test_flag(flags, OPTION_NOTECH)) {
989 if (ast_fileexists(peer_name, NULL, NULL) > 0) {
990 res = ast_streamfile(chan, peer_name, ast_channel_language(chan));
992 res = ast_waitstream(chan, "");
995 ast_autochan_destroy(autochan);
999 res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan));
1002 if (ptr && (num = atoi(ptr))) {
1003 ast_say_digits(chan, num, "", ast_channel_language(chan));
1008 res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
1012 ast_autochan_destroy(autochan);
1013 iter = ast_channel_iterator_destroy(iter);
1015 } else if (res == -2) {
1017 ast_autochan_destroy(autochan);
1018 iter = ast_channel_iterator_destroy(iter);
1020 } else if (res > 1 && spec) {
1021 struct ast_channel *next;
1023 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
1025 if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
1026 next_autochan = ast_autochan_setup(next);
1027 next = ast_channel_unref(next);
1029 /* stay on this channel, if it is still valid */
1030 if (!ast_check_hangup(autochan->chan)) {
1031 next_autochan = ast_autochan_setup(autochan->chan);
1033 /* the channel is gone */
1034 next_autochan = NULL;
1037 } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
1038 ast_autochan_destroy(autochan);
1039 iter = ast_channel_iterator_destroy(iter);
1044 iter = ast_channel_iterator_destroy(iter);
1046 if (res == -1 || ast_check_hangup(chan))
1048 if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
1054 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
1056 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1061 static int chanspy_exec(struct ast_channel *chan, const char *data)
1063 char *myenforced = NULL;
1064 char *mygroup = NULL;
1065 char *recbase = NULL;
1067 struct ast_flags flags;
1068 struct spy_dtmf_options user_options = {
1073 struct ast_format oldwf;
1076 char *mailbox = NULL;
1077 char *name_context = NULL;
1078 AST_DECLARE_APP_ARGS(args,
1080 AST_APP_ARG(options);
1082 char *opts[OPT_ARG_ARRAY_SIZE];
1083 char *parse = ast_strdupa(data);
1085 AST_STANDARD_APP_ARGS(args, parse);
1086 ast_format_clear(&oldwf);
1088 if (args.spec && !strcmp(args.spec, "all"))
1093 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1094 if (ast_test_flag(&flags, OPTION_GROUP))
1095 mygroup = opts[OPT_ARG_GROUP];
1097 if (ast_test_flag(&flags, OPTION_RECORD) &&
1098 !(recbase = opts[OPT_ARG_RECORD]))
1099 recbase = "chanspy";
1101 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1102 tmp = opts[OPT_ARG_EXIT][0];
1103 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1104 user_options.exit = tmp;
1106 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1110 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1111 tmp = opts[OPT_ARG_CYCLE][0];
1112 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1113 user_options.cycle = tmp;
1115 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1119 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1122 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1123 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1128 if (ast_test_flag(&flags, OPTION_PRIVATE))
1129 ast_set_flag(&flags, OPTION_WHISPER);
1131 if (ast_test_flag(&flags, OPTION_ENFORCED))
1132 myenforced = opts[OPT_ARG_ENFORCED];
1134 if (ast_test_flag(&flags, OPTION_NAME)) {
1135 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1137 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1138 mailbox = opts[OPT_ARG_NAME];
1139 *delimiter++ = '\0';
1140 name_context = delimiter;
1142 mailbox = opts[OPT_ARG_NAME];
1147 ast_clear_flag(&flags, AST_FLAGS_ALL);
1150 ast_format_copy(&oldwf, ast_channel_writeformat(chan));
1151 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
1152 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1157 char filename[PATH_MAX];
1159 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1160 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1161 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1166 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1171 if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0)
1172 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1174 if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) {
1175 ast_verb(3, "Stopped spying due to the spied-on channel hanging up.\n");
1181 static int extenspy_exec(struct ast_channel *chan, const char *data)
1183 char *ptr, *exten = NULL;
1184 char *mygroup = NULL;
1185 char *recbase = NULL;
1187 struct ast_flags flags;
1188 struct spy_dtmf_options user_options = {
1193 struct ast_format oldwf;
1196 char *mailbox = NULL;
1197 char *name_context = NULL;
1198 AST_DECLARE_APP_ARGS(args,
1199 AST_APP_ARG(context);
1200 AST_APP_ARG(options);
1202 char *parse = ast_strdupa(data);
1204 AST_STANDARD_APP_ARGS(args, parse);
1205 ast_format_clear(&oldwf);
1207 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1208 exten = args.context;
1212 if (ast_strlen_zero(args.context))
1213 args.context = ast_strdupa(ast_channel_context(chan));
1216 char *opts[OPT_ARG_ARRAY_SIZE];
1219 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1220 if (ast_test_flag(&flags, OPTION_GROUP))
1221 mygroup = opts[OPT_ARG_GROUP];
1223 if (ast_test_flag(&flags, OPTION_RECORD) &&
1224 !(recbase = opts[OPT_ARG_RECORD]))
1225 recbase = "chanspy";
1227 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1228 tmp = opts[OPT_ARG_EXIT][0];
1229 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1230 user_options.exit = tmp;
1232 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1236 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1237 tmp = opts[OPT_ARG_CYCLE][0];
1238 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1239 user_options.cycle = tmp;
1241 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1245 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1248 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1249 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1254 if (ast_test_flag(&flags, OPTION_PRIVATE))
1255 ast_set_flag(&flags, OPTION_WHISPER);
1257 if (ast_test_flag(&flags, OPTION_NAME)) {
1258 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1260 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1261 mailbox = opts[OPT_ARG_NAME];
1262 *delimiter++ = '\0';
1263 name_context = delimiter;
1265 mailbox = opts[OPT_ARG_NAME];
1271 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1272 ast_clear_flag(&flags, AST_FLAGS_ALL);
1275 ast_format_copy(&oldwf, ast_channel_writeformat(chan));
1276 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
1277 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1282 char filename[PATH_MAX];
1284 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1285 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1286 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1292 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1297 if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0)
1298 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1303 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
1305 const char *spec = "DAHDI";
1306 struct ast_flags flags;
1307 struct spy_dtmf_options user_options = {
1312 struct ast_format oldwf;
1314 char *mygroup = NULL;
1316 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1317 ast_clear_flag(&flags, AST_FLAGS_ALL);
1318 ast_format_clear(&oldwf);
1319 if (!ast_strlen_zero(data)) {
1320 mygroup = ast_strdupa(data);
1322 ast_set_flag(&flags, OPTION_DTMF_EXIT);
1323 ast_set_flag(&flags, OPTION_DTMF_CYCLE);
1324 ast_set_flag(&flags, OPTION_DAHDI_SCAN);
1326 ast_format_copy(&oldwf, ast_channel_writeformat(chan));
1327 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
1328 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1332 res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
1334 if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0)
1335 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1340 static int unload_module(void)
1344 res |= ast_unregister_application(app_chan);
1345 res |= ast_unregister_application(app_ext);
1346 res |= ast_unregister_application(app_dahdiscan);
1351 static int load_module(void)
1355 res |= ast_register_application_xml(app_chan, chanspy_exec);
1356 res |= ast_register_application_xml(app_ext, extenspy_exec);
1357 res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
1362 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");