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"
58 #include "asterisk/stasis_channels.h"
59 #include "asterisk/json.h"
60 #include "asterisk/format_cache.h"
62 #define AST_NAME_STRLEN 256
63 #define NUM_SPYGROUPS 128
66 <application name="ChanSpy" language="en_US">
68 Listen to a channel, and optionally whisper into it.
71 <parameter name="chanprefix" />
72 <parameter name="options">
75 <para>Only spy on channels involved in a bridged call.</para>
78 <para>Instead of whispering on a single channel barge in on both
79 channels involved in the call.</para>
82 <argument name="digit" required="true">
83 <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
87 <para>Override the typical numeric DTMF functionality and instead
88 use DTMF to switch between spy modes.</para>
94 <para>whisper mode</para>
97 <para>barge mode</para>
102 <argument name="ext" required="true" />
103 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
104 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
108 <para>Exit when the spied-on channel hangs up.</para>
111 <argument name="grp" required="true">
112 <para>Only spy on channels in which one or more of the groups
113 listed in <replaceable>grp</replaceable> matches one or more groups from the
114 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
116 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
117 either a single group or a colon-delimited list of groups, such
118 as <literal>sales:support:accounting</literal>.</para></note>
120 <option name="n" argsep="@">
121 <para>Say the name of the person being spied on if that person has recorded
122 his/her name. If a context is specified, then that voicemail context will
123 be searched when retrieving the name, otherwise the <literal>default</literal> context
124 be used when searching for the name (i.e. if SIP/1000 is the channel being
125 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
126 for the name).</para>
127 <argument name="mailbox" />
128 <argument name="context" />
131 <para>Only listen to audio coming from this channel.</para>
134 <para>Don't play a beep when beginning to spy on a channel, or speak the
135 selected channel name.</para>
138 <para>Record the session to the monitor spool directory. An optional base for the filename
139 may be specified. The default is <literal>chanspy</literal>.</para>
140 <argument name="basename" />
143 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
144 speaking the selected channel name.</para>
147 <para>Stop when no more channels are left to spy on.</para>
150 <para>The <literal>chanprefix</literal> parameter is a channel uniqueid
151 or fully specified channel name.</para>
154 <argument name="value" />
155 <para>Adjust the initial volume in the range from <literal>-4</literal>
156 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
159 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
160 the spied-on channel.</para>
163 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
164 talk to the spied-on channel but cannot listen to that channel.</para>
167 <argument name="digit" required="true">
168 <para>Specify a DTMF digit that can be used to exit the application while actively
169 spying on a channel. If there is no channel being spied on, the DTMF digit will be
174 <para>Allow the user to exit ChanSpy to a valid single digit
175 numeric extension in the current context or the context
176 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
177 name of the last channel that was spied on will be stored
178 in the <variable>SPY_CHANNEL</variable> variable.</para>
184 <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio
185 coming in and out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
186 only channels beginning with this string will be spied upon.</para>
187 <para>While spying, the following actions may be performed:</para>
188 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
189 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
190 <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
191 to <literal>chanprefix</literal>. For example, executing ChanSpy(Agent) and then dialing the digits '1234#'
192 while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden
193 if the 'd' or 'u' options are used.</para>
194 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
195 single digit extension exists in the correct context ChanSpy will exit to it.
196 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
199 <ref type="application">ExtenSpy</ref>
200 <ref type="managerEvent">ChanSpyStart</ref>
201 <ref type="managerEvent">ChanSpyStop</ref>
204 <application name="ExtenSpy" language="en_US">
206 Listen to a channel, and optionally whisper into it.
209 <parameter name="exten" required="true" argsep="@">
210 <argument name="exten" required="true">
211 <para>Specify extension.</para>
213 <argument name="context">
214 <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
217 <parameter name="options">
220 <para>Only spy on channels involved in a bridged call.</para>
223 <para>Instead of whispering on a single channel barge in on both
224 channels involved in the call.</para>
227 <argument name="digit" required="true">
228 <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
232 <para>Override the typical numeric DTMF functionality and instead
233 use DTMF to switch between spy modes.</para>
236 <para>spy mode</para>
239 <para>whisper mode</para>
242 <para>barge mode</para>
247 <argument name="ext" required="true" />
248 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
249 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
253 <para>Exit when the spied-on channel hangs up.</para>
256 <argument name="grp" required="true">
257 <para>Only spy on channels in which one or more of the groups
258 listed in <replaceable>grp</replaceable> matches one or more groups from the
259 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
261 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
262 either a single group or a colon-delimited list of groups, such
263 as <literal>sales:support:accounting</literal>.</para></note>
265 <option name="n" argsep="@">
266 <para>Say the name of the person being spied on if that person has recorded
267 his/her name. If a context is specified, then that voicemail context will
268 be searched when retrieving the name, otherwise the <literal>default</literal> context
269 be used when searching for the name (i.e. if SIP/1000 is the channel being
270 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
271 for the name).</para>
272 <argument name="mailbox" />
273 <argument name="context" />
276 <para>Only listen to audio coming from this channel.</para>
279 <para>Don't play a beep when beginning to spy on a channel, or speak the
280 selected channel name.</para>
283 <para>Record the session to the monitor spool directory. An optional base for the filename
284 may be specified. The default is <literal>chanspy</literal>.</para>
285 <argument name="basename" />
288 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
289 speaking the selected channel name.</para>
292 <para>Stop when there are no more extensions left to spy on.</para>
295 <argument name="value" />
296 <para>Adjust the initial volume in the range from <literal>-4</literal>
297 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
300 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
301 the spied-on channel.</para>
304 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
305 talk to the spied-on channel but cannot listen to that channel.</para>
308 <argument name="digit" required="true">
309 <para>Specify a DTMF digit that can be used to exit the application while actively
310 spying on a channel. If there is no channel being spied on, the DTMF digit will be
315 <para>Allow the user to exit ChanSpy to a valid single digit
316 numeric extension in the current context or the context
317 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
318 name of the last channel that was spied on will be stored
319 in the <variable>SPY_CHANNEL</variable> variable.</para>
325 <para>This application is used to listen to the audio from an Asterisk channel. This includes
326 the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
327 specified extension will be selected for spying. If the optional context is not supplied,
328 the current channel's context will be used.</para>
329 <para>While spying, the following actions may be performed:</para>
330 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
331 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
332 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
333 single digit extension exists in the correct context ChanSpy will exit to it.
334 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
337 <ref type="application">ChanSpy</ref>
338 <ref type="managerEvent">ChanSpyStart</ref>
339 <ref type="managerEvent">ChanSpyStop</ref>
342 <application name="DAHDIScan" language="en_US">
344 Scan DAHDI channels to monitor calls.
347 <parameter name="group">
348 <para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
352 <para>Allows a call center manager to monitor DAHDI channels in a
353 convenient way. Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
356 <ref type="managerEvent">ChanSpyStart</ref>
357 <ref type="managerEvent">ChanSpyStop</ref>
362 static const char app_chan[] = "ChanSpy";
364 static const char app_ext[] = "ExtenSpy";
366 static const char app_dahdiscan[] = "DAHDIScan";
369 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
370 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
371 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
372 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
373 OPTION_RECORD = (1 << 4),
374 OPTION_WHISPER = (1 << 5),
375 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
376 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
377 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
378 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
379 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
380 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
381 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
382 OPTION_DTMF_SWITCH_MODES = (1 << 13), /* Allow numeric DTMF to switch between chanspy modes */
383 OPTION_DTMF_EXIT = (1 << 14), /* Set DTMF to exit, added for DAHDIScan integration */
384 OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next available channel, (default is '*') */
385 OPTION_DAHDI_SCAN = (1 << 16), /* Scan groups in DAHDIScan mode */
386 OPTION_STOP = (1 << 17),
387 OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */
388 OPTION_UNIQUEID = (1 << 19), /* The chanprefix is a channel uniqueid or fully specified channel name. */
402 AST_APP_OPTIONS(spy_opts, {
403 AST_APP_OPTION('b', OPTION_BRIDGED),
404 AST_APP_OPTION('B', OPTION_BARGE),
405 AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
406 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
407 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
408 AST_APP_OPTION('E', OPTION_EXITONHANGUP),
409 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
410 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
411 AST_APP_OPTION('o', OPTION_READONLY),
412 AST_APP_OPTION('q', OPTION_QUIET),
413 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
414 AST_APP_OPTION('s', OPTION_NOTECH),
415 AST_APP_OPTION('S', OPTION_STOP),
416 AST_APP_OPTION('u', OPTION_UNIQUEID),
417 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
418 AST_APP_OPTION('w', OPTION_WHISPER),
419 AST_APP_OPTION('W', OPTION_PRIVATE),
420 AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT),
421 AST_APP_OPTION('X', OPTION_EXIT),
424 struct chanspy_translation_helper {
426 struct ast_audiohook spy_audiohook;
427 struct ast_audiohook whisper_audiohook;
428 struct ast_audiohook bridge_whisper_audiohook;
431 struct ast_flags flags;
434 struct spy_dtmf_options {
440 static void *spy_alloc(struct ast_channel *chan, void *data)
442 /* just store the data pointer in the channel structure */
446 static void spy_release(struct ast_channel *chan, void *data)
451 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
453 struct chanspy_translation_helper *csth = data;
454 struct ast_frame *f, *cur;
456 ast_audiohook_lock(&csth->spy_audiohook);
457 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
458 /* Channel is already gone more than likely */
459 ast_audiohook_unlock(&csth->spy_audiohook);
463 if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
464 /* Option 'o' was set, so don't mix channel audio */
465 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
467 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
470 ast_audiohook_unlock(&csth->spy_audiohook);
475 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
476 if (ast_write(chan, cur)) {
482 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
483 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
493 static struct ast_generator spygen = {
495 .release = spy_release,
496 .generate = spy_generate,
499 static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
503 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
505 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
506 res = ast_audiohook_attach(autochan->chan, audiohook);
509 ast_channel_lock(autochan->chan);
510 if (ast_channel_is_bridged(autochan->chan)) {
511 ast_channel_set_unbridged_nolock(autochan->chan, 1);
513 ast_channel_unlock(autochan->chan);
518 static void change_spy_mode(const char digit, struct ast_flags *flags)
521 ast_clear_flag(flags, OPTION_WHISPER);
522 ast_clear_flag(flags, OPTION_BARGE);
523 } else if (digit == '5') {
524 ast_clear_flag(flags, OPTION_BARGE);
525 ast_set_flag(flags, OPTION_WHISPER);
526 } else if (digit == '6') {
527 ast_clear_flag(flags, OPTION_WHISPER);
528 ast_set_flag(flags, OPTION_BARGE);
532 static int pack_channel_into_message(struct ast_channel *chan, const char *role,
533 struct ast_multi_channel_blob *payload)
535 RAII_VAR(struct ast_channel_snapshot *, snapshot,
536 ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)),
542 ast_multi_channel_blob_add_channel(payload, role, snapshot);
547 * \brief Publish the chanspy message over Stasis-Core
548 * \param spyer The channel doing the spying
549 * \param spyee Who is being spied upon
550 * \start start If non-zero, the spying is starting. Otherwise, the spyer is
553 static void publish_chanspy_message(struct ast_channel *spyer,
554 struct ast_channel *spyee,
557 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
558 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
559 RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
560 struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type();
563 ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n");
566 blob = ast_json_null();
567 if (!blob || !type) {
571 payload = ast_multi_channel_blob_create(blob);
576 if (pack_channel_into_message(spyer, "spyer_channel", payload)) {
581 if (pack_channel_into_message(spyee, "spyee_channel", payload)) {
586 message = stasis_message_create(type, payload);
590 stasis_publish(ast_channel_topic(spyer), message);
593 static int attach_barge(struct ast_autochan *spyee_autochan,
594 struct ast_autochan **spyee_bridge_autochan, struct ast_audiohook *bridge_whisper_audiohook,
595 const char *spyer_name, const char *name)
598 struct ast_autochan *internal_bridge_autochan;
599 RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(spyee_autochan->chan), ast_channel_cleanup);
605 ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0);
607 internal_bridge_autochan = ast_autochan_setup(bridged);
608 if (!internal_bridge_autochan) {
612 ast_channel_lock(internal_bridge_autochan->chan);
613 if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook)) {
614 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
617 ast_channel_unlock(internal_bridge_autochan->chan);
619 *spyee_bridge_autochan = internal_bridge_autochan;
624 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
625 int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
628 struct chanspy_translation_helper csth;
629 int running = 0, bridge_connected = 0, res, x = 0;
633 struct ast_silence_generator *silgen = NULL;
634 struct ast_autochan *spyee_bridge_autochan = NULL;
635 const char *spyer_name;
637 if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) ||
638 ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) {
642 ast_channel_lock(chan);
643 spyer_name = ast_strdupa(ast_channel_name(chan));
644 ast_channel_unlock(chan);
646 ast_channel_lock(spyee_autochan->chan);
647 name = ast_strdupa(ast_channel_name(spyee_autochan->chan));
648 ast_channel_unlock(spyee_autochan->chan);
650 ast_verb(2, "Spying on channel %s\n", name);
651 publish_chanspy_message(chan, spyee_autochan->chan, 1);
653 memset(&csth, 0, sizeof(csth));
654 ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
656 /* This is the audiohook which gives us the audio off the channel we are
659 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0);
661 if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
662 ast_audiohook_destroy(&csth.spy_audiohook);
666 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
667 /* This audiohook will let us inject audio from our channel into the
668 channel we are currently spying on.
670 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0);
672 if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
673 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
677 ast_channel_lock(chan);
678 ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
679 ast_channel_unlock(chan);
681 csth.volfactor = *volfactor;
683 if (csth.volfactor) {
684 csth.spy_audiohook.options.read_volume = csth.volfactor;
685 csth.spy_audiohook.options.write_volume = csth.volfactor;
690 if (ast_test_flag(flags, OPTION_PRIVATE))
691 silgen = ast_channel_start_silence_generator(chan);
693 ast_activate_generator(chan, &spygen, &csth);
695 /* We can no longer rely on 'spyee' being an actual channel;
696 it can be hung up and freed out from under us. However, the
697 channel destructor will put NULL into our csth.spy.chan
698 field when that happens, so that is our signal that the spyee
699 channel has gone away.
702 /* Note: it is very important that the ast_waitfor() be the first
703 condition in this expression, so that if we wait for some period
704 of time before receiving a frame from our spying channel, we check
705 for hangup on the spied-on channel _after_ knowing that a frame
706 has arrived, since the spied-on channel could have gone away while
709 while (ast_waitfor(chan, -1) > -1 && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
710 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
715 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
716 /* This hook lets us inject audio into the channel that the spyee is currently
717 * bridged with. If the spyee isn't bridged with anything yet, nothing will
718 * be attached and we'll need to continue attempting to attach the barge
720 if (!bridge_connected && attach_barge(spyee_autochan, &spyee_bridge_autochan,
721 &csth.bridge_whisper_audiohook, spyer_name, name) == 0) {
722 bridge_connected = 1;
725 ast_audiohook_lock(&csth.whisper_audiohook);
726 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
727 ast_audiohook_unlock(&csth.whisper_audiohook);
729 if (bridge_connected) {
730 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
731 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
732 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
737 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
738 ast_audiohook_lock(&csth.whisper_audiohook);
739 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
740 ast_audiohook_unlock(&csth.whisper_audiohook);
745 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0;
750 if (x == sizeof(inp))
758 if (ast_test_flag(flags, OPTION_EXIT)) {
762 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
763 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
764 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
768 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
770 } else if (res >= '0' && res <= '9') {
771 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
772 change_spy_mode(res, flags);
778 if (res == user_options->cycle) {
781 } else if (res == user_options->exit) {
784 } else if (res == user_options->volume) {
785 if (!ast_strlen_zero(inp)) {
793 ast_verb(3, "Setting spy volume on %s to %d\n", ast_channel_name(chan), *volfactor);
795 csth.volfactor = *volfactor;
796 csth.spy_audiohook.options.read_volume = csth.volfactor;
797 csth.spy_audiohook.options.write_volume = csth.volfactor;
801 if (ast_test_flag(flags, OPTION_PRIVATE))
802 ast_channel_stop_silence_generator(chan, silgen);
804 ast_deactivate_generator(chan);
806 ast_channel_lock(chan);
807 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
808 ast_channel_unlock(chan);
810 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
811 ast_audiohook_lock(&csth.whisper_audiohook);
812 ast_audiohook_detach(&csth.whisper_audiohook);
813 ast_audiohook_unlock(&csth.whisper_audiohook);
814 ast_audiohook_destroy(&csth.whisper_audiohook);
817 if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
818 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
819 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
820 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
821 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
824 ast_audiohook_lock(&csth.spy_audiohook);
825 ast_audiohook_detach(&csth.spy_audiohook);
826 ast_audiohook_unlock(&csth.spy_audiohook);
827 ast_audiohook_destroy(&csth.spy_audiohook);
829 if (spyee_bridge_autochan) {
830 ast_autochan_destroy(spyee_bridge_autochan);
833 ast_verb(2, "Done Spying on channel %s\n", name);
834 publish_chanspy_message(chan, NULL, 0);
839 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
840 struct ast_autochan *autochan, struct ast_channel *chan)
842 struct ast_channel *next;
843 struct ast_autochan *autochan_store;
844 const size_t pseudo_len = strlen("DAHDI/pseudo");
850 for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) {
851 if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len)
856 autochan_store = ast_autochan_setup(next);
857 ast_channel_unref(next);
859 return autochan_store;
864 static int spy_sayname(struct ast_channel *chan, const char *mailbox, const char *context)
868 mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
869 sprintf(mailbox_id, "%s@%s", mailbox, context); /* Safe */
870 return ast_app_sayname(chan, mailbox_id);
873 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
874 int volfactor, const int fd, struct spy_dtmf_options *user_options,
875 const char *mygroup, const char *myenforced, const char *spec, const char *exten,
876 const char *context, const char *mailbox, const char *name_context)
878 char nameprefix[AST_NAME_STRLEN];
879 char exitcontext[AST_MAX_CONTEXT] = "";
880 signed char zero_volume = 0;
883 int num_spyed_upon = 1;
884 struct ast_channel_iterator *iter = NULL;
886 if (ast_test_flag(flags, OPTION_EXIT)) {
888 ast_channel_lock(chan);
889 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
890 ast_copy_string(exitcontext, c, sizeof(exitcontext));
891 } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
892 ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
894 ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
896 ast_channel_unlock(chan);
899 if (ast_channel_state(chan) != AST_STATE_UP)
902 ast_set_flag(ast_channel_flags(chan), AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
907 struct ast_autochan *autochan = NULL, *next_autochan = NULL;
908 struct ast_channel *prev = NULL;
910 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
911 res = ast_streamfile(chan, "beep", ast_channel_language(chan));
913 res = ast_waitstream(chan, "");
915 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
918 if (!ast_strlen_zero(exitcontext)) {
922 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
925 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
929 /* Set up the iterator we'll be using during this call */
930 if (!ast_strlen_zero(spec)) {
931 if (ast_test_flag(flags, OPTION_UNIQUEID)) {
932 struct ast_channel *unique_chan;
934 unique_chan = ast_channel_get_by_name(spec);
939 iter = ast_channel_iterator_by_name_new(ast_channel_name(unique_chan), 0);
940 ast_channel_unref(unique_chan);
942 iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
944 } else if (!ast_strlen_zero(exten)) {
945 iter = ast_channel_iterator_by_exten_new(exten, context);
947 iter = ast_channel_iterator_all_new();
955 res = ast_waitfordigit(chan, waitms);
957 iter = ast_channel_iterator_destroy(iter);
958 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
961 if (!ast_strlen_zero(exitcontext)) {
965 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
966 iter = ast_channel_iterator_destroy(iter);
969 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
973 /* reset for the next loop around, unless overridden later */
977 for (autochan = next_channel(iter, autochan, chan);
979 prev = autochan->chan, ast_autochan_destroy(autochan),
980 autochan = next_autochan ? next_autochan :
981 next_channel(iter, autochan, chan), next_autochan = NULL) {
983 int ienf = !myenforced;
985 if (autochan->chan == prev) {
986 ast_autochan_destroy(autochan);
990 if (ast_check_hangup(chan)) {
991 ast_autochan_destroy(autochan);
995 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_channel_is_bridged(autochan->chan)) {
999 if (ast_check_hangup(autochan->chan) || ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_SPYING)) {
1005 int num_mygroups = 0;
1006 char dup_group[512];
1007 char dup_mygroup[512];
1008 char *groups[NUM_SPYGROUPS];
1009 char *mygroups[NUM_SPYGROUPS];
1010 const char *group = NULL;
1013 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
1014 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
1015 ARRAY_LEN(mygroups));
1017 /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable
1018 * rather than "SPYGROUP", this check is done to preserve expected behavior */
1019 if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
1020 group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
1022 group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
1025 if (!ast_strlen_zero(group)) {
1026 ast_copy_string(dup_group, group, sizeof(dup_group));
1027 num_groups = ast_app_separate_args(dup_group, ':', groups,
1031 for (y = 0; y < num_mygroups; y++) {
1032 for (x = 0; x < num_groups; x++) {
1033 if (!strcmp(mygroups[y], groups[x])) {
1045 char ext[AST_CHANNEL_NAME + 3];
1049 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
1051 ast_copy_string(ext + 1, ast_channel_name(autochan->chan), sizeof(ext) - 1);
1052 if ((end = strchr(ext, '-'))) {
1059 if (strcasestr(buffer, ext)) {
1068 if (!ast_test_flag(flags, OPTION_QUIET)) {
1069 char peer_name[AST_NAME_STRLEN + 5];
1072 strcpy(peer_name, "spy-");
1073 strncat(peer_name, ast_channel_name(autochan->chan), AST_NAME_STRLEN - 4 - 1);
1074 if ((ptr = strchr(peer_name, '/'))) {
1076 for (s = peer_name; s < ptr; s++) {
1079 if ((s = strchr(ptr, '-'))) {
1084 if (ast_test_flag(flags, OPTION_NAME)) {
1085 const char *local_context = S_OR(name_context, "default");
1086 const char *local_mailbox = S_OR(mailbox, ptr);
1088 if (local_mailbox) {
1089 res = spy_sayname(chan, local_mailbox, local_context);
1094 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
1096 if (!ast_test_flag(flags, OPTION_NOTECH)) {
1097 if (ast_fileexists(peer_name, NULL, NULL) > 0) {
1098 res = ast_streamfile(chan, peer_name, ast_channel_language(chan));
1100 res = ast_waitstream(chan, "");
1103 ast_autochan_destroy(autochan);
1107 res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
1110 if (ptr && (num = atoi(ptr))) {
1111 ast_say_digits(chan, num, "", ast_channel_language(chan));
1116 res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
1120 ast_autochan_destroy(autochan);
1121 iter = ast_channel_iterator_destroy(iter);
1123 } else if (res == -2) {
1125 ast_autochan_destroy(autochan);
1126 iter = ast_channel_iterator_destroy(iter);
1128 } else if (res > 1 && spec && !ast_test_flag(flags, OPTION_UNIQUEID)) {
1129 struct ast_channel *next;
1131 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
1133 if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
1134 next_autochan = ast_autochan_setup(next);
1135 next = ast_channel_unref(next);
1137 /* stay on this channel, if it is still valid */
1138 if (!ast_check_hangup(autochan->chan)) {
1139 next_autochan = ast_autochan_setup(autochan->chan);
1141 /* the channel is gone */
1142 next_autochan = NULL;
1145 } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
1146 ast_autochan_destroy(autochan);
1147 iter = ast_channel_iterator_destroy(iter);
1152 iter = ast_channel_iterator_destroy(iter);
1154 if (res == -1 || ast_check_hangup(chan))
1156 if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
1162 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
1164 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1169 static int chanspy_exec(struct ast_channel *chan, const char *data)
1171 char *myenforced = NULL;
1172 char *mygroup = NULL;
1173 char *recbase = NULL;
1175 struct ast_flags flags;
1176 struct spy_dtmf_options user_options = {
1181 RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
1184 char *mailbox = NULL;
1185 char *name_context = NULL;
1186 AST_DECLARE_APP_ARGS(args,
1188 AST_APP_ARG(options);
1190 char *opts[OPT_ARG_ARRAY_SIZE];
1191 char *parse = ast_strdupa(data);
1193 AST_STANDARD_APP_ARGS(args, parse);
1195 if (args.spec && !strcmp(args.spec, "all"))
1200 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1201 if (ast_test_flag(&flags, OPTION_GROUP))
1202 mygroup = opts[OPT_ARG_GROUP];
1204 if (ast_test_flag(&flags, OPTION_RECORD) &&
1205 !(recbase = opts[OPT_ARG_RECORD]))
1206 recbase = "chanspy";
1208 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1209 tmp = opts[OPT_ARG_EXIT][0];
1210 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1211 user_options.exit = tmp;
1213 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1217 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1218 tmp = opts[OPT_ARG_CYCLE][0];
1219 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1220 user_options.cycle = tmp;
1222 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1226 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1229 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1230 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1235 if (ast_test_flag(&flags, OPTION_PRIVATE))
1236 ast_set_flag(&flags, OPTION_WHISPER);
1238 if (ast_test_flag(&flags, OPTION_ENFORCED))
1239 myenforced = opts[OPT_ARG_ENFORCED];
1241 if (ast_test_flag(&flags, OPTION_NAME)) {
1242 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1244 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1245 mailbox = opts[OPT_ARG_NAME];
1246 *delimiter++ = '\0';
1247 name_context = delimiter;
1249 mailbox = opts[OPT_ARG_NAME];
1254 ast_clear_flag(&flags, AST_FLAGS_ALL);
1257 oldwf = ao2_bump(ast_channel_writeformat(chan));
1258 if (ast_set_write_format(chan, ast_format_slin) < 0) {
1259 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1264 char filename[PATH_MAX];
1266 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1267 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1268 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1273 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1278 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1279 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1281 if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) {
1282 ast_verb(3, "Stopped spying due to the spied-on channel hanging up.\n");
1288 static int extenspy_exec(struct ast_channel *chan, const char *data)
1290 char *ptr, *exten = NULL;
1291 char *mygroup = NULL;
1292 char *recbase = NULL;
1294 struct ast_flags flags;
1295 struct spy_dtmf_options user_options = {
1300 RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
1303 char *mailbox = NULL;
1304 char *name_context = NULL;
1305 AST_DECLARE_APP_ARGS(args,
1306 AST_APP_ARG(context);
1307 AST_APP_ARG(options);
1309 char *parse = ast_strdupa(data);
1311 AST_STANDARD_APP_ARGS(args, parse);
1313 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1314 exten = args.context;
1318 if (ast_strlen_zero(args.context))
1319 args.context = ast_strdupa(ast_channel_context(chan));
1322 char *opts[OPT_ARG_ARRAY_SIZE];
1325 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1326 if (ast_test_flag(&flags, OPTION_GROUP))
1327 mygroup = opts[OPT_ARG_GROUP];
1329 if (ast_test_flag(&flags, OPTION_RECORD) &&
1330 !(recbase = opts[OPT_ARG_RECORD]))
1331 recbase = "chanspy";
1333 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1334 tmp = opts[OPT_ARG_EXIT][0];
1335 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1336 user_options.exit = tmp;
1338 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1342 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1343 tmp = opts[OPT_ARG_CYCLE][0];
1344 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1345 user_options.cycle = tmp;
1347 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1351 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1354 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1355 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1360 if (ast_test_flag(&flags, OPTION_PRIVATE))
1361 ast_set_flag(&flags, OPTION_WHISPER);
1363 if (ast_test_flag(&flags, OPTION_NAME)) {
1364 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1366 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1367 mailbox = opts[OPT_ARG_NAME];
1368 *delimiter++ = '\0';
1369 name_context = delimiter;
1371 mailbox = opts[OPT_ARG_NAME];
1377 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1378 ast_clear_flag(&flags, AST_FLAGS_ALL);
1381 oldwf = ao2_bump(ast_channel_writeformat(chan));
1382 if (ast_set_write_format(chan, ast_format_slin) < 0) {
1383 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1388 char filename[PATH_MAX];
1390 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1391 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1392 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1398 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1403 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1404 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1409 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
1411 const char *spec = "DAHDI";
1412 struct ast_flags flags;
1413 struct spy_dtmf_options user_options = {
1418 struct ast_format *oldwf;
1420 char *mygroup = NULL;
1422 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1423 ast_clear_flag(&flags, AST_FLAGS_ALL);
1425 if (!ast_strlen_zero(data)) {
1426 mygroup = ast_strdupa(data);
1428 ast_set_flag(&flags, OPTION_DTMF_EXIT);
1429 ast_set_flag(&flags, OPTION_DTMF_CYCLE);
1430 ast_set_flag(&flags, OPTION_DAHDI_SCAN);
1432 oldwf = ao2_bump(ast_channel_writeformat(chan));
1433 if (ast_set_write_format(chan, ast_format_slin) < 0) {
1434 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1439 res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
1441 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1442 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1448 static int unload_module(void)
1452 res |= ast_unregister_application(app_chan);
1453 res |= ast_unregister_application(app_ext);
1454 res |= ast_unregister_application(app_dahdiscan);
1459 static int load_module(void)
1463 res |= ast_register_application_xml(app_chan, chanspy_exec);
1464 res |= ast_register_application_xml(app_ext, extenspy_exec);
1465 res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
1470 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");