2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2008, Digium, Inc.
7 * A license has been granted to Digium (via disclaimer) for the use of
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
23 * \brief ChanSpy: Listen in on any channel.
25 * \author Anthony Minessale II <anthmct@yahoo.com>
26 * \author Joshua Colp <jcolp@digium.com>
27 * \author Russell Bryant <russell@digium.com>
29 * \ingroup applications
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/audiohook.h"
42 #include "asterisk/features.h"
43 #include "asterisk/app.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/say.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/translate.h"
48 #include "asterisk/module.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/options.h"
52 #define AST_NAME_STRLEN 256
53 #define NUM_SPYGROUPS 128
56 <application name="ChanSpy" language="en_US">
58 Listen to a channel, and optionally whisper into it.
61 <parameter name="chanprefix" />
62 <parameter name="options">
65 <para>Only spy on channels involved in a bridged call.</para>
68 <para>Instead of whispering on a single channel barge in on both
69 channels involved in the call.</para>
72 <para>Override the typical numeric DTMF functionality and instead
73 use DTMF to switch between spy modes.</para>
79 <para>whisper mode</para>
82 <para>barge mode</para>
87 <argument name="grp" required="true">
88 <para>Only spy on channels in which one or more of the groups
89 listed in <replaceable>grp</replaceable> matches one or more groups from the
90 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
92 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
93 either a single group or a colon-delimited list of groups, such
94 as <literal>sales:support:accounting</literal>.</para></note>
96 <option name="n" argsep="@">
97 <para>Say the name of the person being spied on if that person has recorded
98 his/her name. If a context is specified, then that voicemail context will
99 be searched when retrieving the name, otherwise the <literal>default</literal> context
100 be used when searching for the name (i.e. if SIP/1000 is the channel being
101 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
102 for the name).</para>
103 <argument name="mailbox" />
104 <argument name="context" />
107 <para>Don't play a beep when beginning to spy on a channel, or speak the
108 selected channel name.</para>
111 <para>Record the session to the monitor spool directory. An optional base for the filename
112 may be specified. The default is <literal>chanspy</literal>.</para>
113 <argument name="basename" />
116 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
117 speaking the selected channel name.</para>
120 <argument name="value" />
121 <para>Adjust the initial volume in the range from <literal>-4</literal>
122 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
125 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
126 the spied-on channel.</para>
129 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
130 talk to the spied-on channel but cannot listen to that channel.</para>
133 <para>Only listen to audio coming from this channel.</para>
136 <para>Allow the user to exit ChanSpy to a valid single digit
137 numeric extension in the current context or the context
138 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
139 name of the last channel that was spied on will be stored
140 in the <variable>SPY_CHANNEL</variable> variable.</para>
143 <argument name="ext" required="true" />
144 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
145 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
152 <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio
153 coming in and "out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
154 only channels beginning with this string will be spied upon.</para>
155 <para>While spying, the following actions may be performed:</para>
156 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
157 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
158 <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
159 to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#'
160 while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overriden if the 'd' option
162 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
163 single digit extension exists in the correct context ChanSpy will exit to it.
164 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
167 <application name="ExtenSpy" language="en_US">
169 Listen to a channel, and optionally whisper into it.
172 <parameter name="exten" required="true" argsep="@">
173 <argument name="exten" required="true">
174 <para>Specify extension.</para>
176 <argument name="context">
177 <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
180 <parameter name="options">
183 <para>Only spy on channels involved in a bridged call.</para>
186 <para>Instead of whispering on a single channel barge in on both
187 channels involved in the call.</para>
190 <para>Override the typical numeric DTMF functionality and instead
191 use DTMF to switch between spy modes.</para>
194 <para>spy mode</para>
197 <para>whisper mode</para>
200 <para>barge mode</para>
205 <argument name="grp" required="true">
206 <para>Only spy on channels in which one or more of the groups
207 listed in <replaceable>grp</replaceable> matches one or more groups from the
208 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
210 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
211 either a single group or a colon-delimited list of groups, such
212 as <literal>sales:support:accounting</literal>.</para></note>
214 <option name="n" argsep="@">
215 <para>Say the name of the person being spied on if that person has recorded
216 his/her name. If a context is specified, then that voicemail context will
217 be searched when retrieving the name, otherwise the <literal>default</literal> context
218 be used when searching for the name (i.e. if SIP/1000 is the channel being
219 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
220 for the name).</para>
221 <argument name="mailbox" />
222 <argument name="context" />
225 <para>Don't play a beep when beginning to spy on a channel, or speak the
226 selected channel name.</para>
229 <para>Record the session to the monitor spool directory. An optional base for the filename
230 may be specified. The default is <literal>chanspy</literal>.</para>
231 <argument name="basename" />
234 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
235 speaking the selected channel name.</para>
238 <argument name="value" />
239 <para>Adjust the initial volume in the range from <literal>-4</literal>
240 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
243 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
244 the spied-on channel.</para>
247 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
248 talk to the spied-on channel but cannot listen to that channel.</para>
251 <para>Only listen to audio coming from this channel.</para>
254 <para>Allow the user to exit ChanSpy to a valid single digit
255 numeric extension in the current context or the context
256 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
257 name of the last channel that was spied on will be stored
258 in the <variable>SPY_CHANNEL</variable> variable.</para>
261 <argument name="ext" required="true" />
262 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
263 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
270 <para>This application is used to listen to the audio from an Asterisk channel. This includes
271 the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
272 specified extension will be selected for spying. If the optional context is not supplied,
273 the current channel's context will be used.</para>
274 <para>While spying, the following actions may be performed:</para>
275 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
276 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
277 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
278 single digit extension exists in the correct context ChanSpy will exit to it.
279 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
284 static const char *app_chan = "ChanSpy";
286 static const char *app_ext = "ExtenSpy";
289 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
290 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
291 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
292 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
293 OPTION_RECORD = (1 << 4),
294 OPTION_WHISPER = (1 << 5),
295 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
296 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
297 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
298 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
299 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
300 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
301 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
302 OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */
314 AST_APP_OPTIONS(spy_opts, {
315 AST_APP_OPTION('q', OPTION_QUIET),
316 AST_APP_OPTION('b', OPTION_BRIDGED),
317 AST_APP_OPTION('B', OPTION_BARGE),
318 AST_APP_OPTION('w', OPTION_WHISPER),
319 AST_APP_OPTION('W', OPTION_PRIVATE),
320 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
321 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
322 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
323 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
324 AST_APP_OPTION('o', OPTION_READONLY),
325 AST_APP_OPTION('X', OPTION_EXIT),
326 AST_APP_OPTION('s', OPTION_NOTECH),
327 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
328 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
331 static int next_unique_id_to_use = 0;
333 struct chanspy_translation_helper {
335 struct ast_audiohook spy_audiohook;
336 struct ast_audiohook whisper_audiohook;
337 struct ast_audiohook bridge_whisper_audiohook;
342 static void *spy_alloc(struct ast_channel *chan, void *data)
344 /* just store the data pointer in the channel structure */
348 static void spy_release(struct ast_channel *chan, void *data)
353 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
355 struct chanspy_translation_helper *csth = data;
356 struct ast_frame *f = NULL;
358 ast_audiohook_lock(&csth->spy_audiohook);
359 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
360 /* Channel is already gone more than likely */
361 ast_audiohook_unlock(&csth->spy_audiohook);
365 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
367 ast_audiohook_unlock(&csth->spy_audiohook);
372 if (ast_write(chan, f)) {
378 write(csth->fd, f->data.ptr, f->datalen);
385 static struct ast_generator spygen = {
387 .release = spy_release,
388 .generate = spy_generate,
391 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
394 struct ast_channel *peer = NULL;
396 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
398 res = ast_audiohook_attach(chan, audiohook);
400 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
401 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
407 struct ast_channel *chan;
412 static void change_spy_mode(const char digit, struct ast_flags *flags)
415 ast_clear_flag(flags, OPTION_WHISPER);
416 ast_clear_flag(flags, OPTION_BARGE);
417 } else if (digit == '5') {
418 ast_clear_flag(flags, OPTION_BARGE);
419 ast_set_flag(flags, OPTION_WHISPER);
420 } else if (digit == '6') {
421 ast_clear_flag(flags, OPTION_WHISPER);
422 ast_set_flag(flags, OPTION_BARGE);
426 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
427 int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
429 struct chanspy_translation_helper csth;
430 int running = 0, res, x = 0;
434 struct ast_silence_generator *silgen = NULL;
435 struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
436 const char *spyer_name;
438 ast_channel_lock(chan);
439 spyer_name = ast_strdupa(chan->name);
440 ast_channel_unlock(chan);
442 ast_mutex_lock(&spyee_chanspy_ds->lock);
443 if (spyee_chanspy_ds->chan) {
444 spyee = spyee_chanspy_ds->chan;
445 ast_channel_lock(spyee);
447 ast_mutex_unlock(&spyee_chanspy_ds->lock);
452 /* We now hold the channel lock on spyee */
454 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
455 ast_channel_unlock(spyee);
459 name = ast_strdupa(spyee->name);
460 ast_verb(2, "Spying on channel %s\n", name);
462 memset(&csth, 0, sizeof(csth));
464 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
466 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
467 ast_audiohook_destroy(&csth.spy_audiohook);
468 ast_channel_unlock(spyee);
472 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
473 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
474 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
475 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
477 if ((spyee_bridge = ast_bridged_channel(spyee))) {
478 ast_channel_lock(spyee_bridge);
479 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
480 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
482 ast_channel_unlock(spyee_bridge);
484 ast_channel_unlock(spyee);
487 ast_channel_lock(chan);
488 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
489 ast_channel_unlock(chan);
491 csth.volfactor = *volfactor;
493 if (csth.volfactor) {
494 csth.spy_audiohook.options.read_volume = csth.volfactor;
495 csth.spy_audiohook.options.write_volume = csth.volfactor;
500 if (ast_test_flag(flags, OPTION_PRIVATE))
501 silgen = ast_channel_start_silence_generator(chan);
503 ast_activate_generator(chan, &spygen, &csth);
505 /* We can no longer rely on 'spyee' being an actual channel;
506 it can be hung up and freed out from under us. However, the
507 channel destructor will put NULL into our csth.spy.chan
508 field when that happens, so that is our signal that the spyee
509 channel has gone away.
512 /* Note: it is very important that the ast_waitfor() be the first
513 condition in this expression, so that if we wait for some period
514 of time before receiving a frame from our spying channel, we check
515 for hangup on the spied-on channel _after_ knowing that a frame
516 has arrived, since the spied-on channel could have gone away while
519 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
520 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
525 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
526 ast_audiohook_lock(&csth.whisper_audiohook);
527 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
528 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
529 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
530 ast_audiohook_unlock(&csth.whisper_audiohook);
531 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
534 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
535 ast_audiohook_lock(&csth.whisper_audiohook);
536 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
537 ast_audiohook_unlock(&csth.whisper_audiohook);
542 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
547 if (x == sizeof(inp))
555 if (ast_test_flag(flags, OPTION_EXIT)) {
559 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
560 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
561 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
565 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
567 } else if (res >= '0' && res <= '9') {
568 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
569 change_spy_mode(res, flags);
578 } else if (res == '#') {
579 if (!ast_strlen_zero(inp)) {
587 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
589 csth.volfactor = *volfactor;
590 csth.spy_audiohook.options.read_volume = csth.volfactor;
591 csth.spy_audiohook.options.write_volume = csth.volfactor;
595 if (ast_test_flag(flags, OPTION_PRIVATE))
596 ast_channel_stop_silence_generator(chan, silgen);
598 ast_deactivate_generator(chan);
600 ast_channel_lock(chan);
601 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
602 ast_channel_unlock(chan);
604 ast_audiohook_lock(&csth.whisper_audiohook);
605 ast_audiohook_detach(&csth.whisper_audiohook);
606 ast_audiohook_unlock(&csth.whisper_audiohook);
607 ast_audiohook_destroy(&csth.whisper_audiohook);
608 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
609 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
610 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
611 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
613 ast_audiohook_lock(&csth.spy_audiohook);
614 ast_audiohook_detach(&csth.spy_audiohook);
615 ast_audiohook_unlock(&csth.spy_audiohook);
616 ast_audiohook_destroy(&csth.spy_audiohook);
618 ast_verb(2, "Done Spying on channel %s\n", name);
624 * \note This relies on the embedded lock to be recursive, as it may be called
625 * due to a call to chanspy_ds_free with the lock held there.
627 static void chanspy_ds_destroy(void *data)
629 struct chanspy_ds *chanspy_ds = data;
631 /* Setting chan to be NULL is an atomic operation, but we don't want this
632 * value to change while this lock is held. The lock is held elsewhere
633 * while it performs non-atomic operations with this channel pointer */
635 ast_mutex_lock(&chanspy_ds->lock);
636 chanspy_ds->chan = NULL;
637 ast_mutex_unlock(&chanspy_ds->lock);
640 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
642 struct chanspy_ds *chanspy_ds = data;
644 ast_mutex_lock(&chanspy_ds->lock);
645 chanspy_ds->chan = new_chan;
646 ast_mutex_unlock(&chanspy_ds->lock);
649 static const struct ast_datastore_info chanspy_ds_info = {
651 .destroy = chanspy_ds_destroy,
652 .chan_fixup = chanspy_ds_chan_fixup,
655 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
660 ast_mutex_lock(&chanspy_ds->lock);
661 if (chanspy_ds->chan) {
662 struct ast_datastore *datastore;
663 struct ast_channel *chan;
665 chan = chanspy_ds->chan;
667 ast_channel_lock(chan);
668 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
669 ast_channel_datastore_remove(chan, datastore);
670 /* chanspy_ds->chan is NULL after this call */
671 chanspy_ds_destroy(datastore->data);
672 datastore->data = NULL;
673 ast_datastore_free(datastore);
675 ast_channel_unlock(chan);
677 ast_mutex_unlock(&chanspy_ds->lock);
682 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
683 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
685 struct ast_datastore *datastore = NULL;
687 ast_mutex_lock(&chanspy_ds->lock);
689 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
690 ast_mutex_unlock(&chanspy_ds->lock);
691 chanspy_ds = chanspy_ds_free(chanspy_ds);
692 ast_channel_unlock(chan);
696 chanspy_ds->chan = chan;
697 datastore->data = chanspy_ds;
698 ast_channel_datastore_add(chan, datastore);
703 static struct chanspy_ds *next_channel(struct ast_channel *chan,
704 const struct ast_channel *last, const char *spec,
705 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
707 struct ast_channel *next;
708 const size_t pseudo_len = strlen("DAHDI/pseudo");
711 if (!ast_strlen_zero(spec))
712 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
713 else if (!ast_strlen_zero(exten))
714 next = ast_walk_channel_by_exten_locked(last, exten, context);
716 next = ast_channel_walk_locked(last);
721 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
723 ast_channel_unlock(next);
725 } else if (next == chan) {
727 ast_channel_unlock(next);
731 return setup_chanspy_ds(next, chanspy_ds);
734 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
735 int volfactor, const int fd, const char *mygroup, const char *myenforced,
736 const char *spec, const char *exten, const char *context, const char *mailbox,
737 const char *name_context)
739 char nameprefix[AST_NAME_STRLEN];
740 char peer_name[AST_NAME_STRLEN + 5];
741 char exitcontext[AST_MAX_CONTEXT] = "";
742 signed char zero_volume = 0;
747 int num_spyed_upon = 1;
748 struct chanspy_ds chanspy_ds;
750 if (ast_test_flag(flags, OPTION_EXIT)) {
752 ast_channel_lock(chan);
753 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
754 ast_copy_string(exitcontext, c, sizeof(exitcontext));
755 } else if (!ast_strlen_zero(chan->macrocontext)) {
756 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
758 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
760 ast_channel_unlock(chan);
763 ast_mutex_init(&chanspy_ds.lock);
765 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
767 if (chan->_state != AST_STATE_UP)
770 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
775 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
776 struct ast_channel *prev = NULL, *peer = NULL;
778 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
779 res = ast_streamfile(chan, "beep", chan->language);
781 res = ast_waitstream(chan, "");
783 ast_clear_flag(chan, AST_FLAG_SPYING);
786 if (!ast_strlen_zero(exitcontext)) {
790 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
793 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
797 res = ast_waitfordigit(chan, waitms);
799 ast_clear_flag(chan, AST_FLAG_SPYING);
802 if (!ast_strlen_zero(exitcontext)) {
806 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
809 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
812 /* reset for the next loop around, unless overridden later */
816 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
818 chanspy_ds_free(peer_chanspy_ds), prev = peer,
819 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
820 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
823 char *groups[NUM_SPYGROUPS];
824 char *mygroups[NUM_SPYGROUPS];
827 int num_mygroups = 0;
836 int ienf = !myenforced;
838 peer = peer_chanspy_ds->chan;
840 ast_mutex_unlock(&peer_chanspy_ds->lock);
843 ast_channel_unlock(peer);
844 chanspy_ds_free(peer_chanspy_ds);
848 if (ast_check_hangup(chan)) {
849 ast_channel_unlock(peer);
850 chanspy_ds_free(peer_chanspy_ds);
854 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
855 ast_channel_unlock(peer);
859 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
860 ast_channel_unlock(peer);
865 dup_mygroup = ast_strdupa(mygroup);
866 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
867 ARRAY_LEN(mygroups));
869 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
870 ast_copy_string(dup_group, group, sizeof(dup_group));
871 num_groups = ast_app_separate_args(dup_group, ':', groups,
875 for (y = 0; y < num_mygroups; y++) {
876 for (x = 0; x < num_groups; x++) {
877 if (!strcmp(mygroups[y], groups[x])) {
886 ast_channel_unlock(peer);
892 /* We don't need to allocate more space than just the
893 length of (peer->name) for ext as we will cut the
894 channel name's ending before copying into ext */
896 ext = alloca(strlen(peer->name));
898 form_enforced = alloca(strlen(myenforced) + 3);
900 strcpy(form_enforced, ":");
901 strcat(form_enforced, myenforced);
902 strcat(form_enforced, ":");
904 buffer = ast_strdupa(peer->name);
906 if ((end = strchr(buffer, '-'))) {
914 if (strcasestr(form_enforced, ext))
921 strcpy(peer_name, "spy-");
922 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
923 ptr = strchr(peer_name, '/');
925 ptr = strsep(&ptr, "-");
927 for (s = peer_name; s < ptr; s++)
929 /* We have to unlock the peer channel here to avoid a deadlock.
930 * So, when we need to dereference it again, we have to lock the
931 * datastore and get the pointer from there to see if the channel
933 ast_channel_unlock(peer);
935 if (!ast_test_flag(flags, OPTION_QUIET)) {
936 if (ast_test_flag(flags, OPTION_NAME)) {
937 const char *local_context = S_OR(name_context, "default");
938 const char *local_mailbox = S_OR(mailbox, ptr);
939 res = ast_app_sayname(chan, local_mailbox, local_context);
941 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
942 if (!ast_test_flag(flags, OPTION_NOTECH)) {
943 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
944 res = ast_streamfile(chan, peer_name, chan->language);
946 res = ast_waitstream(chan, "");
949 chanspy_ds_free(peer_chanspy_ds);
953 res = ast_say_character_str(chan, peer_name, "", chan->language);
956 if ((num = atoi(ptr)))
957 ast_say_digits(chan, atoi(ptr), "", chan->language);
961 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
965 chanspy_ds_free(peer_chanspy_ds);
967 } else if (res == -2) {
969 chanspy_ds_free(peer_chanspy_ds);
971 } else if (res > 1 && spec) {
972 struct ast_channel *next;
974 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
976 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
977 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
978 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
980 /* stay on this channel, if it is still valid */
982 ast_mutex_lock(&peer_chanspy_ds->lock);
983 if (peer_chanspy_ds->chan) {
984 ast_channel_lock(peer_chanspy_ds->chan);
985 next_chanspy_ds = peer_chanspy_ds;
986 peer_chanspy_ds = NULL;
988 /* the channel is gone */
989 ast_mutex_unlock(&peer_chanspy_ds->lock);
990 next_chanspy_ds = NULL;
997 if (res == -1 || ast_check_hangup(chan))
1002 ast_clear_flag(chan, AST_FLAG_SPYING);
1004 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1006 ast_mutex_lock(&chanspy_ds.lock);
1007 ast_mutex_unlock(&chanspy_ds.lock);
1008 ast_mutex_destroy(&chanspy_ds.lock);
1013 static int chanspy_exec(struct ast_channel *chan, void *data)
1015 char *myenforced = NULL;
1016 char *mygroup = NULL;
1017 char *recbase = NULL;
1019 struct ast_flags flags;
1023 char *mailbox = NULL;
1024 char *name_context = NULL;
1025 AST_DECLARE_APP_ARGS(args,
1027 AST_APP_ARG(options);
1029 char *opts[OPT_ARG_ARRAY_SIZE];
1031 data = ast_strdupa(data);
1032 AST_STANDARD_APP_ARGS(args, data);
1034 if (args.spec && !strcmp(args.spec, "all"))
1038 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1039 if (ast_test_flag(&flags, OPTION_GROUP))
1040 mygroup = opts[OPT_ARG_GROUP];
1042 if (ast_test_flag(&flags, OPTION_RECORD) &&
1043 !(recbase = opts[OPT_ARG_RECORD]))
1044 recbase = "chanspy";
1046 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1049 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1050 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1055 if (ast_test_flag(&flags, OPTION_PRIVATE))
1056 ast_set_flag(&flags, OPTION_WHISPER);
1058 if (ast_test_flag(&flags, OPTION_ENFORCED))
1059 myenforced = opts[OPT_ARG_ENFORCED];
1061 if (ast_test_flag(&flags, OPTION_NAME)) {
1062 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1064 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1065 mailbox = opts[OPT_ARG_NAME];
1066 *delimiter++ = '\0';
1067 name_context = delimiter;
1069 mailbox = opts[OPT_ARG_NAME];
1076 ast_clear_flag(&flags, AST_FLAGS_ALL);
1078 oldwf = chan->writeformat;
1079 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1080 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1085 char filename[PATH_MAX];
1087 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1088 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1089 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1094 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1099 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1100 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1105 static int extenspy_exec(struct ast_channel *chan, void *data)
1107 char *ptr, *exten = NULL;
1108 char *mygroup = NULL;
1109 char *recbase = NULL;
1111 struct ast_flags flags;
1115 char *mailbox = NULL;
1116 char *name_context = NULL;
1117 AST_DECLARE_APP_ARGS(args,
1118 AST_APP_ARG(context);
1119 AST_APP_ARG(options);
1122 data = ast_strdupa(data);
1124 AST_STANDARD_APP_ARGS(args, data);
1125 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1126 exten = args.context;
1131 if (ast_strlen_zero(args.context))
1132 args.context = ast_strdupa(chan->context);
1135 char *opts[OPT_ARG_ARRAY_SIZE];
1137 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1138 if (ast_test_flag(&flags, OPTION_GROUP))
1139 mygroup = opts[OPT_ARG_GROUP];
1141 if (ast_test_flag(&flags, OPTION_RECORD) &&
1142 !(recbase = opts[OPT_ARG_RECORD]))
1143 recbase = "chanspy";
1145 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1148 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1149 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1154 if (ast_test_flag(&flags, OPTION_PRIVATE))
1155 ast_set_flag(&flags, OPTION_WHISPER);
1158 if (ast_test_flag(&flags, OPTION_NAME)) {
1159 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1161 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1162 mailbox = opts[OPT_ARG_NAME];
1163 *delimiter++ = '\0';
1164 name_context = delimiter;
1166 mailbox = opts[OPT_ARG_NAME];
1172 ast_clear_flag(&flags, AST_FLAGS_ALL);
1174 oldwf = chan->writeformat;
1175 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1176 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1181 char filename[PATH_MAX];
1183 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1184 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1185 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1191 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1196 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1197 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1202 static int unload_module(void)
1206 res |= ast_unregister_application(app_chan);
1207 res |= ast_unregister_application(app_ext);
1212 static int load_module(void)
1216 res |= ast_register_application_xml(app_chan, chanspy_exec);
1217 res |= ast_register_application_xml(app_ext, extenspy_exec);
1222 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");