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)
501 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
503 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
504 return ast_audiohook_attach(autochan->chan, audiohook);
507 static void change_spy_mode(const char digit, struct ast_flags *flags)
510 ast_clear_flag(flags, OPTION_WHISPER);
511 ast_clear_flag(flags, OPTION_BARGE);
512 } else if (digit == '5') {
513 ast_clear_flag(flags, OPTION_BARGE);
514 ast_set_flag(flags, OPTION_WHISPER);
515 } else if (digit == '6') {
516 ast_clear_flag(flags, OPTION_WHISPER);
517 ast_set_flag(flags, OPTION_BARGE);
521 static int pack_channel_into_message(struct ast_channel *chan, const char *role,
522 struct ast_multi_channel_blob *payload)
524 RAII_VAR(struct ast_channel_snapshot *, snapshot,
525 ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)),
531 ast_multi_channel_blob_add_channel(payload, role, snapshot);
536 * \brief Publish the chanspy message over Stasis-Core
537 * \param spyer The channel doing the spying
538 * \param spyee Who is being spied upon
539 * \start start If non-zero, the spying is starting. Otherwise, the spyer is
542 static void publish_chanspy_message(struct ast_channel *spyer,
543 struct ast_channel *spyee,
546 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
547 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
548 RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
549 struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type();
552 ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n");
555 blob = ast_json_null();
556 if (!blob || !type) {
560 payload = ast_multi_channel_blob_create(blob);
565 if (pack_channel_into_message(spyer, "spyer_channel", payload)) {
570 if (pack_channel_into_message(spyee, "spyee_channel", payload)) {
575 message = stasis_message_create(type, payload);
579 stasis_publish(ast_channel_topic(spyer), message);
582 static int attach_barge(struct ast_autochan *spyee_autochan,
583 struct ast_autochan **spyee_bridge_autochan, struct ast_audiohook *bridge_whisper_audiohook,
584 const char *spyer_name, const char *name)
587 struct ast_autochan *internal_bridge_autochan;
588 RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(spyee_autochan->chan), ast_channel_cleanup);
594 ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0);
596 internal_bridge_autochan = ast_autochan_setup(bridged);
597 if (!internal_bridge_autochan) {
601 ast_channel_lock(internal_bridge_autochan->chan);
602 if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook)) {
603 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
606 ast_channel_unlock(internal_bridge_autochan->chan);
608 *spyee_bridge_autochan = internal_bridge_autochan;
613 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
614 int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
617 struct chanspy_translation_helper csth;
618 int running = 0, bridge_connected = 0, res, x = 0;
622 struct ast_silence_generator *silgen = NULL;
623 struct ast_autochan *spyee_bridge_autochan = NULL;
624 const char *spyer_name;
626 if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) ||
627 ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) {
631 ast_channel_lock(chan);
632 spyer_name = ast_strdupa(ast_channel_name(chan));
633 ast_channel_unlock(chan);
635 ast_channel_lock(spyee_autochan->chan);
636 name = ast_strdupa(ast_channel_name(spyee_autochan->chan));
637 ast_channel_unlock(spyee_autochan->chan);
639 ast_verb(2, "Spying on channel %s\n", name);
640 publish_chanspy_message(chan, spyee_autochan->chan, 1);
642 memset(&csth, 0, sizeof(csth));
643 ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
645 /* This is the audiohook which gives us the audio off the channel we are
648 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0);
650 if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
651 ast_audiohook_destroy(&csth.spy_audiohook);
655 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
656 /* This audiohook will let us inject audio from our channel into the
657 channel we are currently spying on.
659 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0);
661 if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
662 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
666 ast_channel_lock(chan);
667 ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
668 ast_channel_unlock(chan);
670 csth.volfactor = *volfactor;
672 if (csth.volfactor) {
673 csth.spy_audiohook.options.read_volume = csth.volfactor;
674 csth.spy_audiohook.options.write_volume = csth.volfactor;
679 if (ast_test_flag(flags, OPTION_PRIVATE))
680 silgen = ast_channel_start_silence_generator(chan);
682 ast_activate_generator(chan, &spygen, &csth);
684 /* We can no longer rely on 'spyee' being an actual channel;
685 it can be hung up and freed out from under us. However, the
686 channel destructor will put NULL into our csth.spy.chan
687 field when that happens, so that is our signal that the spyee
688 channel has gone away.
691 /* Note: it is very important that the ast_waitfor() be the first
692 condition in this expression, so that if we wait for some period
693 of time before receiving a frame from our spying channel, we check
694 for hangup on the spied-on channel _after_ knowing that a frame
695 has arrived, since the spied-on channel could have gone away while
698 while (ast_waitfor(chan, -1) > -1 && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
699 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
704 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
705 /* This hook lets us inject audio into the channel that the spyee is currently
706 * bridged with. If the spyee isn't bridged with anything yet, nothing will
707 * be attached and we'll need to continue attempting to attach the barge
709 if (!bridge_connected && attach_barge(spyee_autochan, &spyee_bridge_autochan,
710 &csth.bridge_whisper_audiohook, spyer_name, name) == 0) {
711 bridge_connected = 1;
714 ast_audiohook_lock(&csth.whisper_audiohook);
715 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
716 ast_audiohook_unlock(&csth.whisper_audiohook);
718 if (bridge_connected) {
719 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
720 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
721 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
726 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
727 ast_audiohook_lock(&csth.whisper_audiohook);
728 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
729 ast_audiohook_unlock(&csth.whisper_audiohook);
734 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0;
739 if (x == sizeof(inp))
747 if (ast_test_flag(flags, OPTION_EXIT)) {
751 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
752 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
753 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
757 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
759 } else if (res >= '0' && res <= '9') {
760 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
761 change_spy_mode(res, flags);
767 if (res == user_options->cycle) {
770 } else if (res == user_options->exit) {
773 } else if (res == user_options->volume) {
774 if (!ast_strlen_zero(inp)) {
782 ast_verb(3, "Setting spy volume on %s to %d\n", ast_channel_name(chan), *volfactor);
784 csth.volfactor = *volfactor;
785 csth.spy_audiohook.options.read_volume = csth.volfactor;
786 csth.spy_audiohook.options.write_volume = csth.volfactor;
790 if (ast_test_flag(flags, OPTION_PRIVATE))
791 ast_channel_stop_silence_generator(chan, silgen);
793 ast_deactivate_generator(chan);
795 ast_channel_lock(chan);
796 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
797 ast_channel_unlock(chan);
799 if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
800 ast_audiohook_lock(&csth.whisper_audiohook);
801 ast_audiohook_detach(&csth.whisper_audiohook);
802 ast_audiohook_unlock(&csth.whisper_audiohook);
803 ast_audiohook_destroy(&csth.whisper_audiohook);
806 if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
807 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
808 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
809 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
810 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
813 ast_audiohook_lock(&csth.spy_audiohook);
814 ast_audiohook_detach(&csth.spy_audiohook);
815 ast_audiohook_unlock(&csth.spy_audiohook);
816 ast_audiohook_destroy(&csth.spy_audiohook);
818 if (spyee_bridge_autochan) {
819 ast_autochan_destroy(spyee_bridge_autochan);
822 ast_verb(2, "Done Spying on channel %s\n", name);
823 publish_chanspy_message(chan, NULL, 0);
828 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
829 struct ast_autochan *autochan, struct ast_channel *chan)
831 struct ast_channel *next;
832 struct ast_autochan *autochan_store;
833 const size_t pseudo_len = strlen("DAHDI/pseudo");
839 for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) {
840 if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len)
845 autochan_store = ast_autochan_setup(next);
846 ast_channel_unref(next);
848 return autochan_store;
853 static int spy_sayname(struct ast_channel *chan, const char *mailbox, const char *context)
857 mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
858 sprintf(mailbox_id, "%s@%s", mailbox, context); /* Safe */
859 return ast_app_sayname(chan, mailbox_id);
862 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
863 int volfactor, const int fd, struct spy_dtmf_options *user_options,
864 const char *mygroup, const char *myenforced, const char *spec, const char *exten,
865 const char *context, const char *mailbox, const char *name_context)
867 char nameprefix[AST_NAME_STRLEN];
868 char exitcontext[AST_MAX_CONTEXT] = "";
869 signed char zero_volume = 0;
872 int num_spyed_upon = 1;
873 struct ast_channel_iterator *iter = NULL;
875 if (ast_test_flag(flags, OPTION_EXIT)) {
877 ast_channel_lock(chan);
878 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
879 ast_copy_string(exitcontext, c, sizeof(exitcontext));
880 } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
881 ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
883 ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
885 ast_channel_unlock(chan);
888 if (ast_channel_state(chan) != AST_STATE_UP)
891 ast_set_flag(ast_channel_flags(chan), AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
896 struct ast_autochan *autochan = NULL, *next_autochan = NULL;
897 struct ast_channel *prev = NULL;
899 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
900 res = ast_streamfile(chan, "beep", ast_channel_language(chan));
902 res = ast_waitstream(chan, "");
904 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
907 if (!ast_strlen_zero(exitcontext)) {
911 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
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 /* Set up the iterator we'll be using during this call */
919 if (!ast_strlen_zero(spec)) {
920 if (ast_test_flag(flags, OPTION_UNIQUEID)) {
921 struct ast_channel *unique_chan;
923 unique_chan = ast_channel_get_by_name(spec);
928 iter = ast_channel_iterator_by_name_new(ast_channel_name(unique_chan), 0);
929 ast_channel_unref(unique_chan);
931 iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
933 } else if (!ast_strlen_zero(exten)) {
934 iter = ast_channel_iterator_by_exten_new(exten, context);
936 iter = ast_channel_iterator_all_new();
944 res = ast_waitfordigit(chan, waitms);
946 iter = ast_channel_iterator_destroy(iter);
947 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
950 if (!ast_strlen_zero(exitcontext)) {
954 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
955 iter = ast_channel_iterator_destroy(iter);
958 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
962 /* reset for the next loop around, unless overridden later */
966 for (autochan = next_channel(iter, autochan, chan);
968 prev = autochan->chan, ast_autochan_destroy(autochan),
969 autochan = next_autochan ? next_autochan :
970 next_channel(iter, autochan, chan), next_autochan = NULL) {
972 int ienf = !myenforced;
974 if (autochan->chan == prev) {
975 ast_autochan_destroy(autochan);
979 if (ast_check_hangup(chan)) {
980 ast_autochan_destroy(autochan);
984 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_channel_is_bridged(autochan->chan)) {
988 if (ast_check_hangup(autochan->chan) || ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_SPYING)) {
994 int num_mygroups = 0;
996 char dup_mygroup[512];
997 char *groups[NUM_SPYGROUPS];
998 char *mygroups[NUM_SPYGROUPS];
999 const char *group = NULL;
1002 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
1003 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
1004 ARRAY_LEN(mygroups));
1006 /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable
1007 * rather than "SPYGROUP", this check is done to preserve expected behavior */
1008 if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
1009 group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
1011 group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
1014 if (!ast_strlen_zero(group)) {
1015 ast_copy_string(dup_group, group, sizeof(dup_group));
1016 num_groups = ast_app_separate_args(dup_group, ':', groups,
1020 for (y = 0; y < num_mygroups; y++) {
1021 for (x = 0; x < num_groups; x++) {
1022 if (!strcmp(mygroups[y], groups[x])) {
1034 char ext[AST_CHANNEL_NAME + 3];
1038 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
1040 ast_copy_string(ext + 1, ast_channel_name(autochan->chan), sizeof(ext) - 1);
1041 if ((end = strchr(ext, '-'))) {
1048 if (strcasestr(buffer, ext)) {
1057 if (!ast_test_flag(flags, OPTION_QUIET)) {
1058 char peer_name[AST_NAME_STRLEN + 5];
1061 strcpy(peer_name, "spy-");
1062 strncat(peer_name, ast_channel_name(autochan->chan), AST_NAME_STRLEN - 4 - 1);
1063 if ((ptr = strchr(peer_name, '/'))) {
1065 for (s = peer_name; s < ptr; s++) {
1068 if ((s = strchr(ptr, '-'))) {
1073 if (ast_test_flag(flags, OPTION_NAME)) {
1074 const char *local_context = S_OR(name_context, "default");
1075 const char *local_mailbox = S_OR(mailbox, ptr);
1077 if (local_mailbox) {
1078 res = spy_sayname(chan, local_mailbox, local_context);
1083 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
1085 if (!ast_test_flag(flags, OPTION_NOTECH)) {
1086 if (ast_fileexists(peer_name, NULL, NULL) > 0) {
1087 res = ast_streamfile(chan, peer_name, ast_channel_language(chan));
1089 res = ast_waitstream(chan, "");
1092 ast_autochan_destroy(autochan);
1096 res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
1099 if (ptr && (num = atoi(ptr))) {
1100 ast_say_digits(chan, num, "", ast_channel_language(chan));
1105 res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
1109 ast_autochan_destroy(autochan);
1110 iter = ast_channel_iterator_destroy(iter);
1112 } else if (res == -2) {
1114 ast_autochan_destroy(autochan);
1115 iter = ast_channel_iterator_destroy(iter);
1117 } else if (res > 1 && spec && !ast_test_flag(flags, OPTION_UNIQUEID)) {
1118 struct ast_channel *next;
1120 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
1122 if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
1123 next_autochan = ast_autochan_setup(next);
1124 next = ast_channel_unref(next);
1126 /* stay on this channel, if it is still valid */
1127 if (!ast_check_hangup(autochan->chan)) {
1128 next_autochan = ast_autochan_setup(autochan->chan);
1130 /* the channel is gone */
1131 next_autochan = NULL;
1134 } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
1135 ast_autochan_destroy(autochan);
1136 iter = ast_channel_iterator_destroy(iter);
1141 iter = ast_channel_iterator_destroy(iter);
1143 if (res == -1 || ast_check_hangup(chan))
1145 if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
1151 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SPYING);
1153 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1158 static int chanspy_exec(struct ast_channel *chan, const char *data)
1160 char *myenforced = NULL;
1161 char *mygroup = NULL;
1162 char *recbase = NULL;
1164 struct ast_flags flags;
1165 struct spy_dtmf_options user_options = {
1170 RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
1173 char *mailbox = NULL;
1174 char *name_context = NULL;
1175 AST_DECLARE_APP_ARGS(args,
1177 AST_APP_ARG(options);
1179 char *opts[OPT_ARG_ARRAY_SIZE];
1180 char *parse = ast_strdupa(data);
1182 AST_STANDARD_APP_ARGS(args, parse);
1184 if (args.spec && !strcmp(args.spec, "all"))
1189 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1190 if (ast_test_flag(&flags, OPTION_GROUP))
1191 mygroup = opts[OPT_ARG_GROUP];
1193 if (ast_test_flag(&flags, OPTION_RECORD) &&
1194 !(recbase = opts[OPT_ARG_RECORD]))
1195 recbase = "chanspy";
1197 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1198 tmp = opts[OPT_ARG_EXIT][0];
1199 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1200 user_options.exit = tmp;
1202 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1206 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1207 tmp = opts[OPT_ARG_CYCLE][0];
1208 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1209 user_options.cycle = tmp;
1211 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1215 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1218 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1219 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1224 if (ast_test_flag(&flags, OPTION_PRIVATE))
1225 ast_set_flag(&flags, OPTION_WHISPER);
1227 if (ast_test_flag(&flags, OPTION_ENFORCED))
1228 myenforced = opts[OPT_ARG_ENFORCED];
1230 if (ast_test_flag(&flags, OPTION_NAME)) {
1231 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1233 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1234 mailbox = opts[OPT_ARG_NAME];
1235 *delimiter++ = '\0';
1236 name_context = delimiter;
1238 mailbox = opts[OPT_ARG_NAME];
1243 ast_clear_flag(&flags, AST_FLAGS_ALL);
1246 oldwf = ao2_bump(ast_channel_writeformat(chan));
1247 if (ast_set_write_format(chan, ast_format_slin) < 0) {
1248 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1253 char filename[PATH_MAX];
1255 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1256 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1257 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1262 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1267 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1268 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1270 if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) {
1271 ast_verb(3, "Stopped spying due to the spied-on channel hanging up.\n");
1277 static int extenspy_exec(struct ast_channel *chan, const char *data)
1279 char *ptr, *exten = NULL;
1280 char *mygroup = NULL;
1281 char *recbase = NULL;
1283 struct ast_flags flags;
1284 struct spy_dtmf_options user_options = {
1289 RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
1292 char *mailbox = NULL;
1293 char *name_context = NULL;
1294 AST_DECLARE_APP_ARGS(args,
1295 AST_APP_ARG(context);
1296 AST_APP_ARG(options);
1298 char *parse = ast_strdupa(data);
1300 AST_STANDARD_APP_ARGS(args, parse);
1302 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1303 exten = args.context;
1307 if (ast_strlen_zero(args.context))
1308 args.context = ast_strdupa(ast_channel_context(chan));
1311 char *opts[OPT_ARG_ARRAY_SIZE];
1314 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1315 if (ast_test_flag(&flags, OPTION_GROUP))
1316 mygroup = opts[OPT_ARG_GROUP];
1318 if (ast_test_flag(&flags, OPTION_RECORD) &&
1319 !(recbase = opts[OPT_ARG_RECORD]))
1320 recbase = "chanspy";
1322 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1323 tmp = opts[OPT_ARG_EXIT][0];
1324 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1325 user_options.exit = tmp;
1327 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
1331 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1332 tmp = opts[OPT_ARG_CYCLE][0];
1333 if (strchr("0123456789*#", tmp) && tmp != '\0') {
1334 user_options.cycle = tmp;
1336 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
1340 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1343 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
1344 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1349 if (ast_test_flag(&flags, OPTION_PRIVATE))
1350 ast_set_flag(&flags, OPTION_WHISPER);
1352 if (ast_test_flag(&flags, OPTION_NAME)) {
1353 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1355 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1356 mailbox = opts[OPT_ARG_NAME];
1357 *delimiter++ = '\0';
1358 name_context = delimiter;
1360 mailbox = opts[OPT_ARG_NAME];
1366 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1367 ast_clear_flag(&flags, AST_FLAGS_ALL);
1370 oldwf = ao2_bump(ast_channel_writeformat(chan));
1371 if (ast_set_write_format(chan, ast_format_slin) < 0) {
1372 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1377 char filename[PATH_MAX];
1379 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1380 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1381 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1387 res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1392 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1393 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1398 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
1400 const char *spec = "DAHDI";
1401 struct ast_flags flags;
1402 struct spy_dtmf_options user_options = {
1407 struct ast_format *oldwf;
1409 char *mygroup = NULL;
1411 /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
1412 ast_clear_flag(&flags, AST_FLAGS_ALL);
1414 if (!ast_strlen_zero(data)) {
1415 mygroup = ast_strdupa(data);
1417 ast_set_flag(&flags, OPTION_DTMF_EXIT);
1418 ast_set_flag(&flags, OPTION_DTMF_CYCLE);
1419 ast_set_flag(&flags, OPTION_DAHDI_SCAN);
1421 oldwf = ao2_bump(ast_channel_writeformat(chan));
1422 if (ast_set_write_format(chan, ast_format_slin) < 0) {
1423 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1428 res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
1430 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1431 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1437 static int unload_module(void)
1441 res |= ast_unregister_application(app_chan);
1442 res |= ast_unregister_application(app_ext);
1443 res |= ast_unregister_application(app_dahdiscan);
1448 static int load_module(void)
1452 res |= ast_register_application_xml(app_chan, chanspy_exec);
1453 res |= ast_register_application_xml(app_ext, extenspy_exec);
1454 res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
1459 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");