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"
61 #define AST_NAME_STRLEN 256
62 #define NUM_SPYGROUPS 128
65 <application name="ChanSpy" language="en_US">
67 Listen to a channel, and optionally whisper into it.
70 <parameter name="chanprefix" />
71 <parameter name="options">
74 <para>Only spy on channels involved in a bridged call.</para>
77 <para>Instead of whispering on a single channel barge in on both
78 channels involved in the call.</para>
81 <argument name="digit" required="true">
82 <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
86 <para>Override the typical numeric DTMF functionality and instead
87 use DTMF to switch between spy modes.</para>
93 <para>whisper mode</para>
96 <para>barge mode</para>
101 <argument name="ext" required="true" />
102 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
103 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
107 <para>Exit when the spied-on channel hangs up.</para>
110 <argument name="grp" required="true">
111 <para>Only spy on channels in which one or more of the groups
112 listed in <replaceable>grp</replaceable> matches one or more groups from the
113 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
115 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
116 either a single group or a colon-delimited list of groups, such
117 as <literal>sales:support:accounting</literal>.</para></note>
119 <option name="n" argsep="@">
120 <para>Say the name of the person being spied on if that person has recorded
121 his/her name. If a context is specified, then that voicemail context will
122 be searched when retrieving the name, otherwise the <literal>default</literal> context
123 be used when searching for the name (i.e. if SIP/1000 is the channel being
124 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
125 for the name).</para>
126 <argument name="mailbox" />
127 <argument name="context" />
130 <para>Only listen to audio coming from this channel.</para>
133 <para>Don't play a beep when beginning to spy on a channel, or speak the
134 selected channel name.</para>
137 <para>Record the session to the monitor spool directory. An optional base for the filename
138 may be specified. The default is <literal>chanspy</literal>.</para>
139 <argument name="basename" />
142 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
143 speaking the selected channel name.</para>
146 <para>Stop when no more channels are left to spy on.</para>
149 <argument name="value" />
150 <para>Adjust the initial volume in the range from <literal>-4</literal>
151 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
154 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
155 the spied-on channel.</para>
158 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
159 talk to the spied-on channel but cannot listen to that channel.</para>
162 <argument name="digit" required="true">
163 <para>Specify a DTMF digit that can be used to exit the application.</para>
167 <para>Allow the user to exit ChanSpy to a valid single digit
168 numeric extension in the current context or the context
169 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
170 name of the last channel that was spied on will be stored
171 in the <variable>SPY_CHANNEL</variable> variable.</para>
177 <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio
178 coming in and out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
179 only channels beginning with this string will be spied upon.</para>
180 <para>While spying, the following actions may be performed:</para>
181 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
182 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
183 <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
184 to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#'
185 while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden if the 'd' option
187 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
188 single digit extension exists in the correct context ChanSpy will exit to it.
189 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
192 <ref type="application">ExtenSpy</ref>
193 <ref type="managerEvent">ChanSpyStart</ref>
194 <ref type="managerEvent">ChanSpyStop</ref>
197 <application name="ExtenSpy" language="en_US">
199 Listen to a channel, and optionally whisper into it.
202 <parameter name="exten" required="true" argsep="@">
203 <argument name="exten" required="true">
204 <para>Specify extension.</para>
206 <argument name="context">
207 <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
210 <parameter name="options">
213 <para>Only spy on channels involved in a bridged call.</para>
216 <para>Instead of whispering on a single channel barge in on both
217 channels involved in the call.</para>
220 <argument name="digit" required="true">
221 <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
225 <para>Override the typical numeric DTMF functionality and instead
226 use DTMF to switch between spy modes.</para>
229 <para>spy mode</para>
232 <para>whisper mode</para>
235 <para>barge mode</para>
240 <argument name="ext" required="true" />
241 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
242 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
246 <para>Exit when the spied-on channel hangs up.</para>
249 <argument name="grp" required="true">
250 <para>Only spy on channels in which one or more of the groups
251 listed in <replaceable>grp</replaceable> matches one or more groups from the
252 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
254 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
255 either a single group or a colon-delimited list of groups, such
256 as <literal>sales:support:accounting</literal>.</para></note>
258 <option name="n" argsep="@">
259 <para>Say the name of the person being spied on if that person has recorded
260 his/her name. If a context is specified, then that voicemail context will
261 be searched when retrieving the name, otherwise the <literal>default</literal> context
262 be used when searching for the name (i.e. if SIP/1000 is the channel being
263 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
264 for the name).</para>
265 <argument name="mailbox" />
266 <argument name="context" />
269 <para>Only listen to audio coming from this channel.</para>
272 <para>Don't play a beep when beginning to spy on a channel, or speak the
273 selected channel name.</para>
276 <para>Record the session to the monitor spool directory. An optional base for the filename
277 may be specified. The default is <literal>chanspy</literal>.</para>
278 <argument name="basename" />
281 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
282 speaking the selected channel name.</para>
285 <para>Stop when there are no more extensions left to spy on.</para>
288 <argument name="value" />
289 <para>Adjust the initial volume in the range from <literal>-4</literal>
290 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
293 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
294 the spied-on channel.</para>
297 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
298 talk to the spied-on channel but cannot listen to that channel.</para>
301 <argument name="digit" required="true">
302 <para>Specify a DTMF digit that can be used to exit the application.</para>
306 <para>Allow the user to exit ChanSpy to a valid single digit
307 numeric extension in the current context or the context
308 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
309 name of the last channel that was spied on will be stored
310 in the <variable>SPY_CHANNEL</variable> variable.</para>
316 <para>This application is used to listen to the audio from an Asterisk channel. This includes
317 the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
318 specified extension will be selected for spying. If the optional context is not supplied,
319 the current channel's context will be used.</para>
320 <para>While spying, the following actions may be performed:</para>
321 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
322 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
323 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
324 single digit extension exists in the correct context ChanSpy will exit to it.
325 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
328 <ref type="application">ChanSpy</ref>
329 <ref type="managerEvent">ChanSpyStart</ref>
330 <ref type="managerEvent">ChanSpyStop</ref>
333 <application name="DAHDIScan" language="en_US">
335 Scan DAHDI channels to monitor calls.
338 <parameter name="group">
339 <para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
343 <para>Allows a call center manager to monitor DAHDI channels in a
344 convenient way. Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
347 <ref type="managerEvent">ChanSpyStart</ref>
348 <ref type="managerEvent">ChanSpyStop</ref>
353 static const char app_chan[] = "ChanSpy";
355 static const char app_ext[] = "ExtenSpy";
357 static const char app_dahdiscan[] = "DAHDIScan";
360 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
361 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
362 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
363 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
364 OPTION_RECORD = (1 << 4),
365 OPTION_WHISPER = (1 << 5),
366 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
367 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
368 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
369 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
370 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
371 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
372 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
373 OPTION_DTMF_SWITCH_MODES = (1 << 13), /* Allow numeric DTMF to switch between chanspy modes */
374 OPTION_DTMF_EXIT = (1 << 14), /* Set DTMF to exit, added for DAHDIScan integration */
375 OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next available channel, (default is '*') */
376 OPTION_DAHDI_SCAN = (1 << 16), /* Scan groups in DAHDIScan mode */
377 OPTION_STOP = (1 << 17),
378 OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */
392 AST_APP_OPTIONS(spy_opts, {
393 AST_APP_OPTION('b', OPTION_BRIDGED),
394 AST_APP_OPTION('B', OPTION_BARGE),
395 AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
396 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
397 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
398 AST_APP_OPTION('E', OPTION_EXITONHANGUP),
399 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
400 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
401 AST_APP_OPTION('o', OPTION_READONLY),
402 AST_APP_OPTION('q', OPTION_QUIET),
403 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
404 AST_APP_OPTION('s', OPTION_NOTECH),
405 AST_APP_OPTION('S', OPTION_STOP),
406 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
407 AST_APP_OPTION('w', OPTION_WHISPER),
408 AST_APP_OPTION('W', OPTION_PRIVATE),
409 AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT),
410 AST_APP_OPTION('X', OPTION_EXIT),
413 struct chanspy_translation_helper {
415 struct ast_audiohook spy_audiohook;
416 struct ast_audiohook whisper_audiohook;
417 struct ast_audiohook bridge_whisper_audiohook;
420 struct ast_flags flags;
423 struct spy_dtmf_options {
429 static void *spy_alloc(struct ast_channel *chan, void *data)
431 /* just store the data pointer in the channel structure */
435 static void spy_release(struct ast_channel *chan, void *data)
440 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
442 struct chanspy_translation_helper *csth = data;
443 struct ast_frame *f, *cur;
444 struct ast_format format_slin;
446 ast_format_set(&format_slin, AST_FORMAT_SLINEAR, 0);
448 ast_audiohook_lock(&csth->spy_audiohook);
449 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
450 /* Channel is already gone more than likely */
451 ast_audiohook_unlock(&csth->spy_audiohook);
455 if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
456 /* Option 'o' was set, so don't mix channel audio */
457 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, &format_slin);
459 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, &format_slin);
462 ast_audiohook_unlock(&csth->spy_audiohook);
467 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
468 if (ast_write(chan, cur)) {
474 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
475 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
485 static struct ast_generator spygen = {
487 .release = spy_release,
488 .generate = spy_generate,
491 static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
495 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
497 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
498 res = ast_audiohook_attach(autochan->chan, audiohook);
501 ast_channel_lock(autochan->chan);
502 if (ast_channel_is_bridged(autochan->chan)) {
503 ast_softhangup_nolock(autochan->chan, AST_SOFTHANGUP_UNBRIDGE);
505 ast_channel_unlock(autochan->chan);
510 static void change_spy_mode(const char digit, struct ast_flags *flags)
513 ast_clear_flag(flags, OPTION_WHISPER);
514 ast_clear_flag(flags, OPTION_BARGE);
515 } else if (digit == '5') {
516 ast_clear_flag(flags, OPTION_BARGE);
517 ast_set_flag(flags, OPTION_WHISPER);
518 } else if (digit == '6') {
519 ast_clear_flag(flags, OPTION_WHISPER);
520 ast_set_flag(flags, OPTION_BARGE);
524 static int pack_channel_into_message(struct ast_channel *chan, const char *role,
525 struct ast_multi_channel_blob *payload)
527 RAII_VAR(struct ast_channel_snapshot *, snapshot,
528 ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)),
534 ast_multi_channel_blob_add_channel(payload, role, snapshot);
539 * \brief Publish the chanspy message over Stasis-Core
540 * \param spyer The channel doing the spying
541 * \param spyee Who is being spied upon
542 * \start start If non-zero, the spying is starting. Otherwise, the spyer is
545 static void publish_chanspy_message(struct ast_channel *spyer,
546 struct ast_channel *spyee,
549 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
550 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
551 RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
554 ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n");
557 blob = ast_json_null();
562 payload = ast_multi_channel_blob_create(blob);
567 if (pack_channel_into_message(spyer, "spyer_channel", payload)) {
572 if (pack_channel_into_message(spyee, "spyee_channel", payload)) {
577 message = stasis_message_create(
578 start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type(),
583 stasis_publish(ast_channel_topic(spyer), message);
586 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
587 int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
590 struct chanspy_translation_helper csth;
591 int running = 0, res, x = 0;
595 struct ast_silence_generator *silgen = NULL;
596 struct ast_autochan *spyee_bridge_autochan = NULL;
597 const char *spyer_name;
599 if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) ||
600 ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) {
604 ast_channel_lock(chan);
605 spyer_name = ast_strdupa(ast_channel_name(chan));
606 ast_channel_unlock(chan);
608 ast_channel_lock(spyee_autochan->chan);
609 name = ast_strdupa(ast_channel_name(spyee_autochan->chan));
610 ast_channel_unlock(spyee_autochan->chan);
612 ast_verb(2, "Spying on channel %s\n", name);
613 publish_chanspy_message(chan, spyee_autochan->chan, 1);
615 memset(&csth, 0, sizeof(csth));
616 ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
618 /* This is the audiohook which gives us the audio off the channel we are
621 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0);
623 if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
624 ast_audiohook_destroy(&csth.spy_audiohook);
628 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
629 /* This audiohook will let us inject audio from our channel into the
630 channel we are currently spying on.
632 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0);
634 if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
635 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
639 if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
640 RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(spyee_autochan->chan), ast_channel_cleanup);
642 /* And this hook lets us inject audio into the channel that the spied on
643 channel is currently bridged with.
645 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0);
647 if ((spyee_bridge_autochan = ast_autochan_setup(bridged))) {
648 ast_channel_lock(spyee_bridge_autochan->chan);
649 if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
650 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
652 ast_channel_unlock(spyee_bridge_autochan->chan);
656 ast_channel_lock(chan);
657 ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
658 ast_channel_unlock(chan);
660 csth.volfactor = *volfactor;
662 if (csth.volfactor) {
663 csth.spy_audiohook.options.read_volume = csth.volfactor;
664 csth.spy_audiohook.options.write_volume = csth.volfactor;
669 if (ast_test_flag(flags, OPTION_PRIVATE))
670 silgen = ast_channel_start_silence_generator(chan);
672 ast_activate_generator(chan, &spygen, &csth);
674 /* We can no longer rely on 'spyee' being an actual channel;
675 it can be hung up and freed out from under us. However, the
676 channel destructor will put NULL into our csth.spy.chan
677 field when that happens, so that is our signal that the spyee
678 channel has gone away.
681 /* Note: it is very important that the ast_waitfor() be the first
682 condition in this expression, so that if we wait for some period
683 of time before receiving a frame from our spying channel, we check
684 for hangup on the spied-on channel _after_ knowing that a frame
685 has arrived, since the spied-on channel could have gone away while
688 while (ast_waitfor(chan, -1) > -1 && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
689 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
694 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
695 ast_audiohook_lock(&csth.whisper_audiohook);
696 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
697 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
698 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
699 ast_audiohook_unlock(&csth.whisper_audiohook);
700 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
703 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
704 ast_audiohook_lock(&csth.whisper_audiohook);
705 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
706 ast_audiohook_unlock(&csth.whisper_audiohook);
711 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0;
716 if (x == sizeof(inp))
724 if (ast_test_flag(flags, OPTION_EXIT)) {
728 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
729 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
730 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
734 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
736 } else if (res >= '0' && res <= '9') {
737 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
738 change_spy_mode(res, flags);
744 if (res == user_options->cycle) {
747 } else if (res == user_options->exit) {
750 } else if (res == user_options->volume) {
751 if (!ast_strlen_zero(inp)) {
759 ast_verb(3, "Setting spy volume on %s to %d\n", ast_channel_name(chan), *volfactor);
761 csth.volfactor = *volfactor;
762 csth.spy_audiohook.options.read_volume = csth.volfactor;
763 csth.spy_audiohook.options.write_volume = csth.volfactor;
767 if (ast_test_flag(flags, OPTION_PRIVATE))
768 ast_channel_stop_silence_generator(chan, silgen);
770 ast_deactivate_generator(chan);
772 ast_channel_lock(chan);
773 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
774 ast_channel_unlock(chan);
776 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
777 ast_audiohook_lock(&csth.whisper_audiohook);
778 ast_audiohook_detach(&csth.whisper_audiohook);
779 ast_audiohook_unlock(&csth.whisper_audiohook);
780 ast_audiohook_destroy(&csth.whisper_audiohook);
783 if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
784 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
785 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
786 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
787 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
790 ast_audiohook_lock(&csth.spy_audiohook);
791 ast_audiohook_detach(&csth.spy_audiohook);
792 ast_audiohook_unlock(&csth.spy_audiohook);
793 ast_audiohook_destroy(&csth.spy_audiohook);
795 if (spyee_bridge_autochan) {
796 ast_autochan_destroy(spyee_bridge_autochan);
799 ast_verb(2, "Done Spying on channel %s\n", name);
800 publish_chanspy_message(chan, NULL, 0);
805 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
806 struct ast_autochan *autochan, struct ast_channel *chan)
808 struct ast_channel *next;
809 struct ast_autochan *autochan_store;
810 const size_t pseudo_len = strlen("DAHDI/pseudo");
816 for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) {
817 if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len)
822 autochan_store = ast_autochan_setup(next);
823 ast_channel_unref(next);
825 return autochan_store;
830 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
831 int volfactor, const int fd, struct spy_dtmf_options *user_options,
832 const char *mygroup, const char *myenforced, const char *spec, const char *exten,
833 const char *context, const char *mailbox, const char *name_context)
835 char nameprefix[AST_NAME_STRLEN];
836 char exitcontext[AST_MAX_CONTEXT] = "";
837 signed char zero_volume = 0;
840 int num_spyed_upon = 1;
841 struct ast_channel_iterator *iter = NULL;
843 if (ast_test_flag(flags, OPTION_EXIT)) {
845 ast_channel_lock(chan);
846 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
847 ast_copy_string(exitcontext, c, sizeof(exitcontext));
848 } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
849 ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
851 ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
853 ast_channel_unlock(chan);
856 if (ast_channel_state(chan) != AST_STATE_UP)
859 ast_set_flag(ast_channel_flags(chan), AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
864 struct ast_autochan *autochan = NULL, *next_autochan = NULL;
865 struct ast_channel *prev = NULL;
867 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
868 res = ast_streamfile(chan, "beep", ast_channel_language(chan));
870 res = ast_waitstream(chan, "");
872 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
875 if (!ast_strlen_zero(exitcontext)) {
879 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
882 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
886 /* Set up the iterator we'll be using during this call */
887 if (!ast_strlen_zero(spec)) {
888 iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
889 } else if (!ast_strlen_zero(exten)) {
890 iter = ast_channel_iterator_by_exten_new(exten, context);
892 iter = ast_channel_iterator_all_new();
900 res = ast_waitfordigit(chan, waitms);
902 iter = ast_channel_iterator_destroy(iter);
903 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
906 if (!ast_strlen_zero(exitcontext)) {
910 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
911 iter = ast_channel_iterator_destroy(iter);
914 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
918 /* reset for the next loop around, unless overridden later */
922 for (autochan = next_channel(iter, autochan, chan);
924 prev = autochan->chan, ast_autochan_destroy(autochan),
925 autochan = next_autochan ? next_autochan :
926 next_channel(iter, autochan, chan), next_autochan = NULL) {
928 int ienf = !myenforced;
930 if (autochan->chan == prev) {
931 ast_autochan_destroy(autochan);
935 if (ast_check_hangup(chan)) {
936 ast_autochan_destroy(autochan);
940 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_channel_is_bridged(autochan->chan)) {
944 if (ast_check_hangup(autochan->chan) || ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_SPYING)) {
950 int num_mygroups = 0;
952 char dup_mygroup[512];
953 char *groups[NUM_SPYGROUPS];
954 char *mygroups[NUM_SPYGROUPS];
955 const char *group = NULL;
958 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
959 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
960 ARRAY_LEN(mygroups));
962 /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable
963 * rather than "SPYGROUP", this check is done to preserve expected behavior */
964 if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
965 group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
967 group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
970 if (!ast_strlen_zero(group)) {
971 ast_copy_string(dup_group, group, sizeof(dup_group));
972 num_groups = ast_app_separate_args(dup_group, ':', groups,
976 for (y = 0; y < num_mygroups; y++) {
977 for (x = 0; x < num_groups; x++) {
978 if (!strcmp(mygroups[y], groups[x])) {
990 char ext[AST_CHANNEL_NAME + 3];
994 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
996 ast_copy_string(ext + 1, ast_channel_name(autochan->chan), sizeof(ext) - 1);
997 if ((end = strchr(ext, '-'))) {
1004 if (strcasestr(buffer, ext)) {
1013 if (!ast_test_flag(flags, OPTION_QUIET)) {
1014 char peer_name[AST_NAME_STRLEN + 5];
1017 strcpy(peer_name, "spy-");
1018 strncat(peer_name, ast_channel_name(autochan->chan), AST_NAME_STRLEN - 4 - 1);
1019 if ((ptr = strchr(peer_name, '/'))) {
1021 for (s = peer_name; s < ptr; s++) {
1024 if ((s = strchr(ptr, '-'))) {
1029 if (ast_test_flag(flags, OPTION_NAME)) {
1030 const char *local_context = S_OR(name_context, "default");
1031 const char *local_mailbox = S_OR(mailbox, ptr);
1032 if (local_mailbox) {
1033 res = ast_app_sayname(chan, local_mailbox, local_context);
1038 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
1040 if (!ast_test_flag(flags, OPTION_NOTECH)) {
1041 if (ast_fileexists(peer_name, NULL, NULL) > 0) {
1042 res = ast_streamfile(chan, peer_name, ast_channel_language(chan));
1044 res = ast_waitstream(chan, "");
1047 ast_autochan_destroy(autochan);
1051 res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
1054 if (ptr && (num = atoi(ptr))) {
1055 ast_say_digits(chan, num, "", ast_channel_language(chan));
1060 res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
1064 ast_autochan_destroy(autochan);
1065 iter = ast_channel_iterator_destroy(iter);
1067 } else if (res == -2) {
1069 ast_autochan_destroy(autochan);
1070 iter = ast_channel_iterator_destroy(iter);
1072 } else if (res > 1 && spec) {
1073 struct ast_channel *next;
1075 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
1077 if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
1078 next_autochan = ast_autochan_setup(next);
1079 next = ast_channel_unref(next);
1081 /* stay on this channel, if it is still valid */
1082 if (!ast_check_hangup(autochan->chan)) {
1083 next_autochan = ast_autochan_setup(autochan->chan);
1085 /* the channel is gone */
1086 next_autochan = NULL;
1089 } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
1090 ast_autochan_destroy(autochan);
1091 iter = ast_channel_iterator_destroy(iter);
1096 iter = ast_channel_iterator_destroy(iter);
1098 if (res == -1 || ast_check_hangup(chan))
1100 if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
1106 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
1108 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1113 static int chanspy_exec(struct ast_channel *chan, const char *data)
1115 char *myenforced = NULL;
1116 char *mygroup = NULL;
1117 char *recbase = NULL;
1119 struct ast_flags flags;
1120 struct spy_dtmf_options user_options = {
1125 struct ast_format oldwf;
1128 char *mailbox = NULL;
1129 char *name_context = NULL;
1130 AST_DECLARE_APP_ARGS(args,
1132 AST_APP_ARG(options);
1134 char *opts[OPT_ARG_ARRAY_SIZE];
1135 char *parse = ast_strdupa(data);
1137 AST_STANDARD_APP_ARGS(args, parse);
1138 ast_format_clear(&oldwf);
1140 if (args.spec && !strcmp(args.spec, "all"))
1145 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1146 if (ast_test_flag(&flags, OPTION_GROUP))
1147 mygroup = opts[OPT_ARG_GROUP];
1149 if (ast_test_flag(&flags, OPTION_RECORD) &&
1150 !(recbase = opts[OPT_ARG_RECORD]))
1151 recbase = "chanspy";
1153 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1154 tmp = opts[OPT_ARG_EXIT][0];
1155 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1156 user_options.exit = tmp;
1158 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1162 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1163 tmp = opts[OPT_ARG_CYCLE][0];
1164 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1165 user_options.cycle = tmp;
1167 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1171 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1174 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1175 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1180 if (ast_test_flag(&flags, OPTION_PRIVATE))
1181 ast_set_flag(&flags, OPTION_WHISPER);
1183 if (ast_test_flag(&flags, OPTION_ENFORCED))
1184 myenforced = opts[OPT_ARG_ENFORCED];
1186 if (ast_test_flag(&flags, OPTION_NAME)) {
1187 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1189 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1190 mailbox = opts[OPT_ARG_NAME];
1191 *delimiter++ = '\0';
1192 name_context = delimiter;
1194 mailbox = opts[OPT_ARG_NAME];
1199 ast_clear_flag(&flags, AST_FLAGS_ALL);
1202 ast_format_copy(&oldwf, ast_channel_writeformat(chan));
1203 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
1204 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1209 char filename[PATH_MAX];
1211 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1212 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1213 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1218 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1223 if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0)
1224 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1226 if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) {
1227 ast_verb(3, "Stopped spying due to the spied-on channel hanging up.\n");
1233 static int extenspy_exec(struct ast_channel *chan, const char *data)
1235 char *ptr, *exten = NULL;
1236 char *mygroup = NULL;
1237 char *recbase = NULL;
1239 struct ast_flags flags;
1240 struct spy_dtmf_options user_options = {
1245 struct ast_format oldwf;
1248 char *mailbox = NULL;
1249 char *name_context = NULL;
1250 AST_DECLARE_APP_ARGS(args,
1251 AST_APP_ARG(context);
1252 AST_APP_ARG(options);
1254 char *parse = ast_strdupa(data);
1256 AST_STANDARD_APP_ARGS(args, parse);
1257 ast_format_clear(&oldwf);
1259 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1260 exten = args.context;
1264 if (ast_strlen_zero(args.context))
1265 args.context = ast_strdupa(ast_channel_context(chan));
1268 char *opts[OPT_ARG_ARRAY_SIZE];
1271 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1272 if (ast_test_flag(&flags, OPTION_GROUP))
1273 mygroup = opts[OPT_ARG_GROUP];
1275 if (ast_test_flag(&flags, OPTION_RECORD) &&
1276 !(recbase = opts[OPT_ARG_RECORD]))
1277 recbase = "chanspy";
1279 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1280 tmp = opts[OPT_ARG_EXIT][0];
1281 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1282 user_options.exit = tmp;
1284 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1288 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1289 tmp = opts[OPT_ARG_CYCLE][0];
1290 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1291 user_options.cycle = tmp;
1293 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1297 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1300 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1301 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1306 if (ast_test_flag(&flags, OPTION_PRIVATE))
1307 ast_set_flag(&flags, OPTION_WHISPER);
1309 if (ast_test_flag(&flags, OPTION_NAME)) {
1310 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1312 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1313 mailbox = opts[OPT_ARG_NAME];
1314 *delimiter++ = '\0';
1315 name_context = delimiter;
1317 mailbox = opts[OPT_ARG_NAME];
1323 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1324 ast_clear_flag(&flags, AST_FLAGS_ALL);
1327 ast_format_copy(&oldwf, ast_channel_writeformat(chan));
1328 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
1329 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1334 char filename[PATH_MAX];
1336 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1337 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1338 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1344 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1349 if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0)
1350 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1355 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
1357 const char *spec = "DAHDI";
1358 struct ast_flags flags;
1359 struct spy_dtmf_options user_options = {
1364 struct ast_format oldwf;
1366 char *mygroup = NULL;
1368 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1369 ast_clear_flag(&flags, AST_FLAGS_ALL);
1370 ast_format_clear(&oldwf);
1371 if (!ast_strlen_zero(data)) {
1372 mygroup = ast_strdupa(data);
1374 ast_set_flag(&flags, OPTION_DTMF_EXIT);
1375 ast_set_flag(&flags, OPTION_DTMF_CYCLE);
1376 ast_set_flag(&flags, OPTION_DAHDI_SCAN);
1378 ast_format_copy(&oldwf, ast_channel_writeformat(chan));
1379 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
1380 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1384 res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
1386 if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0)
1387 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1392 static int unload_module(void)
1396 res |= ast_unregister_application(app_chan);
1397 res |= ast_unregister_application(app_ext);
1398 res |= ast_unregister_application(app_dahdiscan);
1403 static int load_module(void)
1407 res |= ast_register_application_xml(app_chan, chanspy_exec);
1408 res |= ast_register_application_xml(app_ext, extenspy_exec);
1409 res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
1414 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");