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$")
39 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/audiohook.h"
43 #include "asterisk/features.h"
44 #include "asterisk/app.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/say.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/translate.h"
49 #include "asterisk/module.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/options.h"
53 #define AST_NAME_STRLEN 256
54 #define NUM_SPYGROUPS 128
57 <application name="ChanSpy" language="en_US">
59 Listen to a channel, and optionally whisper into it.
62 <parameter name="chanprefix" />
63 <parameter name="options">
66 <para>Only spy on channels involved in a bridged call.</para>
69 <para>Instead of whispering on a single channel barge in on both
70 channels involved in the call.</para>
73 <para>Override the typical numeric DTMF functionality and instead
74 use DTMF to switch between spy modes.</para>
80 <para>whisper mode</para>
83 <para>barge mode</para>
88 <argument name="grp" required="true">
89 <para>Only spy on channels in which one or more of the groups
90 listed in <replaceable>grp</replaceable> matches one or more groups from the
91 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
93 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
94 either a single group or a colon-delimited list of groups, such
95 as <literal>sales:support:accounting</literal>.</para></note>
97 <option name="n" argsep="@">
98 <para>Say the name of the person being spied on if that person has recorded
99 his/her name. If a context is specified, then that voicemail context will
100 be searched when retrieving the name, otherwise the <literal>default</literal> context
101 be used when searching for the name (i.e. if SIP/1000 is the channel being
102 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
103 for the name).</para>
104 <argument name="mailbox" />
105 <argument name="context" />
108 <para>Don't play a beep when beginning to spy on a channel, or speak the
109 selected channel name.</para>
112 <para>Record the session to the monitor spool directory. An optional base for the filename
113 may be specified. The default is <literal>chanspy</literal>.</para>
114 <argument name="basename" />
117 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
118 speaking the selected channel name.</para>
121 <argument name="value" />
122 <para>Adjust the initial volume in the range from <literal>-4</literal>
123 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
126 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
127 the spied-on channel.</para>
130 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
131 talk to the spied-on channel but cannot listen to that channel.</para>
134 <para>Only listen to audio coming from this channel.</para>
137 <para>Allow the user to exit ChanSpy to a valid single digit
138 numeric extension in the current context or the context
139 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
140 name of the last channel that was spied on will be stored
141 in the <variable>SPY_CHANNEL</variable> variable.</para>
144 <argument name="ext" required="true" />
145 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
146 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
153 <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio
154 coming in and "out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
155 only channels beginning with this string will be spied upon.</para>
156 <para>While spying, the following actions may be performed:</para>
157 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
158 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
159 <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
160 to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#'
161 while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden if the 'd' option
163 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
164 single digit extension exists in the correct context ChanSpy will exit to it.
165 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
168 <ref type="application">ExtenSpy</ref>
171 <application name="ExtenSpy" language="en_US">
173 Listen to a channel, and optionally whisper into it.
176 <parameter name="exten" required="true" argsep="@">
177 <argument name="exten" required="true">
178 <para>Specify extension.</para>
180 <argument name="context">
181 <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
184 <parameter name="options">
187 <para>Only spy on channels involved in a bridged call.</para>
190 <para>Instead of whispering on a single channel barge in on both
191 channels involved in the call.</para>
194 <para>Override the typical numeric DTMF functionality and instead
195 use DTMF to switch between spy modes.</para>
198 <para>spy mode</para>
201 <para>whisper mode</para>
204 <para>barge mode</para>
209 <argument name="grp" required="true">
210 <para>Only spy on channels in which one or more of the groups
211 listed in <replaceable>grp</replaceable> matches one or more groups from the
212 <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
214 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain
215 either a single group or a colon-delimited list of groups, such
216 as <literal>sales:support:accounting</literal>.</para></note>
218 <option name="n" argsep="@">
219 <para>Say the name of the person being spied on if that person has recorded
220 his/her name. If a context is specified, then that voicemail context will
221 be searched when retrieving the name, otherwise the <literal>default</literal> context
222 be used when searching for the name (i.e. if SIP/1000 is the channel being
223 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
224 for the name).</para>
225 <argument name="mailbox" />
226 <argument name="context" />
229 <para>Don't play a beep when beginning to spy on a channel, or speak the
230 selected channel name.</para>
233 <para>Record the session to the monitor spool directory. An optional base for the filename
234 may be specified. The default is <literal>chanspy</literal>.</para>
235 <argument name="basename" />
238 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
239 speaking the selected channel name.</para>
242 <argument name="value" />
243 <para>Adjust the initial volume in the range from <literal>-4</literal>
244 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
247 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
248 the spied-on channel.</para>
251 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
252 talk to the spied-on channel but cannot listen to that channel.</para>
255 <para>Only listen to audio coming from this channel.</para>
258 <para>Allow the user to exit ChanSpy to a valid single digit
259 numeric extension in the current context or the context
260 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
261 name of the last channel that was spied on will be stored
262 in the <variable>SPY_CHANNEL</variable> variable.</para>
265 <argument name="ext" required="true" />
266 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
267 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited
274 <para>This application is used to listen to the audio from an Asterisk channel. This includes
275 the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
276 specified extension will be selected for spying. If the optional context is not supplied,
277 the current channel's context will be used.</para>
278 <para>While spying, the following actions may be performed:</para>
279 <para> - Dialing <literal>#</literal> cycles the volume level.</para>
280 <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
281 <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
282 single digit extension exists in the correct context ChanSpy will exit to it.
283 This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
286 <ref type="application">ChanSpy</ref>
291 static const char *app_chan = "ChanSpy";
293 static const char *app_ext = "ExtenSpy";
296 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
297 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
298 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
299 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
300 OPTION_RECORD = (1 << 4),
301 OPTION_WHISPER = (1 << 5),
302 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
303 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
304 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
305 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
306 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
307 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
308 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
309 OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */
321 AST_APP_OPTIONS(spy_opts, {
322 AST_APP_OPTION('q', OPTION_QUIET),
323 AST_APP_OPTION('b', OPTION_BRIDGED),
324 AST_APP_OPTION('B', OPTION_BARGE),
325 AST_APP_OPTION('w', OPTION_WHISPER),
326 AST_APP_OPTION('W', OPTION_PRIVATE),
327 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
328 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
329 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
330 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
331 AST_APP_OPTION('o', OPTION_READONLY),
332 AST_APP_OPTION('X', OPTION_EXIT),
333 AST_APP_OPTION('s', OPTION_NOTECH),
334 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
335 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
338 static int next_unique_id_to_use = 0;
340 struct chanspy_translation_helper {
342 struct ast_audiohook spy_audiohook;
343 struct ast_audiohook whisper_audiohook;
344 struct ast_audiohook bridge_whisper_audiohook;
349 static void *spy_alloc(struct ast_channel *chan, void *data)
351 /* just store the data pointer in the channel structure */
355 static void spy_release(struct ast_channel *chan, void *data)
360 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
362 struct chanspy_translation_helper *csth = data;
363 struct ast_frame *f = NULL;
365 ast_audiohook_lock(&csth->spy_audiohook);
366 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
367 /* Channel is already gone more than likely */
368 ast_audiohook_unlock(&csth->spy_audiohook);
372 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
374 ast_audiohook_unlock(&csth->spy_audiohook);
379 if (ast_write(chan, f)) {
385 if (write(csth->fd, f->data.ptr, f->datalen) < 0) {
386 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
395 static struct ast_generator spygen = {
397 .release = spy_release,
398 .generate = spy_generate,
401 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
404 struct ast_channel *peer = NULL;
406 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
408 res = ast_audiohook_attach(chan, audiohook);
410 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
411 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
417 struct ast_channel *chan;
422 static void change_spy_mode(const char digit, struct ast_flags *flags)
425 ast_clear_flag(flags, OPTION_WHISPER);
426 ast_clear_flag(flags, OPTION_BARGE);
427 } else if (digit == '5') {
428 ast_clear_flag(flags, OPTION_BARGE);
429 ast_set_flag(flags, OPTION_WHISPER);
430 } else if (digit == '6') {
431 ast_clear_flag(flags, OPTION_WHISPER);
432 ast_set_flag(flags, OPTION_BARGE);
436 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
437 int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
439 struct chanspy_translation_helper csth;
440 int running = 0, res, x = 0;
444 struct ast_silence_generator *silgen = NULL;
445 struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
446 const char *spyer_name;
448 ast_channel_lock(chan);
449 spyer_name = ast_strdupa(chan->name);
450 ast_channel_unlock(chan);
452 ast_mutex_lock(&spyee_chanspy_ds->lock);
453 if (spyee_chanspy_ds->chan) {
454 spyee = spyee_chanspy_ds->chan;
455 ast_channel_lock(spyee);
457 ast_mutex_unlock(&spyee_chanspy_ds->lock);
462 /* We now hold the channel lock on spyee */
464 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
465 ast_channel_unlock(spyee);
469 name = ast_strdupa(spyee->name);
470 ast_verb(2, "Spying on channel %s\n", name);
472 memset(&csth, 0, sizeof(csth));
474 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
476 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
477 ast_audiohook_destroy(&csth.spy_audiohook);
478 ast_channel_unlock(spyee);
482 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
483 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
484 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
485 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
487 if ((spyee_bridge = ast_bridged_channel(spyee))) {
488 ast_channel_lock(spyee_bridge);
489 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
490 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
492 ast_channel_unlock(spyee_bridge);
494 ast_channel_unlock(spyee);
497 ast_channel_lock(chan);
498 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
499 ast_channel_unlock(chan);
501 csth.volfactor = *volfactor;
503 if (csth.volfactor) {
504 csth.spy_audiohook.options.read_volume = csth.volfactor;
505 csth.spy_audiohook.options.write_volume = csth.volfactor;
510 if (ast_test_flag(flags, OPTION_PRIVATE))
511 silgen = ast_channel_start_silence_generator(chan);
513 ast_activate_generator(chan, &spygen, &csth);
515 /* We can no longer rely on 'spyee' being an actual channel;
516 it can be hung up and freed out from under us. However, the
517 channel destructor will put NULL into our csth.spy.chan
518 field when that happens, so that is our signal that the spyee
519 channel has gone away.
522 /* Note: it is very important that the ast_waitfor() be the first
523 condition in this expression, so that if we wait for some period
524 of time before receiving a frame from our spying channel, we check
525 for hangup on the spied-on channel _after_ knowing that a frame
526 has arrived, since the spied-on channel could have gone away while
529 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
530 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
535 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
536 ast_audiohook_lock(&csth.whisper_audiohook);
537 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
538 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
539 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
540 ast_audiohook_unlock(&csth.whisper_audiohook);
541 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
544 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
545 ast_audiohook_lock(&csth.whisper_audiohook);
546 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
547 ast_audiohook_unlock(&csth.whisper_audiohook);
552 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
557 if (x == sizeof(inp))
565 if (ast_test_flag(flags, OPTION_EXIT)) {
569 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
570 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
571 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
575 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
577 } else if (res >= '0' && res <= '9') {
578 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
579 change_spy_mode(res, flags);
588 } else if (res == '#') {
589 if (!ast_strlen_zero(inp)) {
597 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
599 csth.volfactor = *volfactor;
600 csth.spy_audiohook.options.read_volume = csth.volfactor;
601 csth.spy_audiohook.options.write_volume = csth.volfactor;
605 if (ast_test_flag(flags, OPTION_PRIVATE))
606 ast_channel_stop_silence_generator(chan, silgen);
608 ast_deactivate_generator(chan);
610 ast_channel_lock(chan);
611 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
612 ast_channel_unlock(chan);
614 ast_audiohook_lock(&csth.whisper_audiohook);
615 ast_audiohook_detach(&csth.whisper_audiohook);
616 ast_audiohook_unlock(&csth.whisper_audiohook);
617 ast_audiohook_destroy(&csth.whisper_audiohook);
619 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
620 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
621 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
622 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
624 ast_audiohook_lock(&csth.spy_audiohook);
625 ast_audiohook_detach(&csth.spy_audiohook);
626 ast_audiohook_unlock(&csth.spy_audiohook);
627 ast_audiohook_destroy(&csth.spy_audiohook);
629 ast_verb(2, "Done Spying on channel %s\n", name);
635 * \note This relies on the embedded lock to be recursive, as it may be called
636 * due to a call to chanspy_ds_free with the lock held there.
638 static void chanspy_ds_destroy(void *data)
640 struct chanspy_ds *chanspy_ds = data;
642 /* Setting chan to be NULL is an atomic operation, but we don't want this
643 * value to change while this lock is held. The lock is held elsewhere
644 * while it performs non-atomic operations with this channel pointer */
646 ast_mutex_lock(&chanspy_ds->lock);
647 chanspy_ds->chan = NULL;
648 ast_mutex_unlock(&chanspy_ds->lock);
651 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
653 struct chanspy_ds *chanspy_ds = data;
655 ast_mutex_lock(&chanspy_ds->lock);
656 chanspy_ds->chan = new_chan;
657 ast_mutex_unlock(&chanspy_ds->lock);
660 static const struct ast_datastore_info chanspy_ds_info = {
662 .destroy = chanspy_ds_destroy,
663 .chan_fixup = chanspy_ds_chan_fixup,
666 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
671 ast_mutex_lock(&chanspy_ds->lock);
672 if (chanspy_ds->chan) {
673 struct ast_datastore *datastore;
674 struct ast_channel *chan;
676 chan = chanspy_ds->chan;
678 ast_channel_lock(chan);
679 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
680 ast_channel_datastore_remove(chan, datastore);
681 /* chanspy_ds->chan is NULL after this call */
682 chanspy_ds_destroy(datastore->data);
683 datastore->data = NULL;
684 ast_datastore_free(datastore);
686 ast_channel_unlock(chan);
688 ast_mutex_unlock(&chanspy_ds->lock);
693 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
694 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
696 struct ast_datastore *datastore = NULL;
698 ast_mutex_lock(&chanspy_ds->lock);
700 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
701 ast_mutex_unlock(&chanspy_ds->lock);
702 chanspy_ds = chanspy_ds_free(chanspy_ds);
703 ast_channel_unlock(chan);
707 chanspy_ds->chan = chan;
708 datastore->data = chanspy_ds;
709 ast_channel_datastore_add(chan, datastore);
714 static struct chanspy_ds *next_channel(struct ast_channel *chan,
715 const struct ast_channel *last, const char *spec,
716 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
718 struct ast_channel *next;
719 const size_t pseudo_len = strlen("DAHDI/pseudo");
722 if (!ast_strlen_zero(spec))
723 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
724 else if (!ast_strlen_zero(exten))
725 next = ast_walk_channel_by_exten_locked(last, exten, context);
727 next = ast_channel_walk_locked(last);
732 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
734 ast_channel_unlock(next);
736 } else if (next == chan) {
738 ast_channel_unlock(next);
742 return setup_chanspy_ds(next, chanspy_ds);
745 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
746 int volfactor, const int fd, const char *mygroup, const char *myenforced,
747 const char *spec, const char *exten, const char *context, const char *mailbox,
748 const char *name_context)
750 char nameprefix[AST_NAME_STRLEN];
751 char peer_name[AST_NAME_STRLEN + 5];
752 char exitcontext[AST_MAX_CONTEXT] = "";
753 signed char zero_volume = 0;
758 int num_spyed_upon = 1;
759 struct chanspy_ds chanspy_ds = { 0, };
761 if (ast_test_flag(flags, OPTION_EXIT)) {
763 ast_channel_lock(chan);
764 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
765 ast_copy_string(exitcontext, c, sizeof(exitcontext));
766 } else if (!ast_strlen_zero(chan->macrocontext)) {
767 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
769 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
771 ast_channel_unlock(chan);
774 ast_mutex_init(&chanspy_ds.lock);
776 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
778 if (chan->_state != AST_STATE_UP)
781 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
786 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
787 struct ast_channel *prev = NULL, *peer = NULL;
789 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
790 res = ast_streamfile(chan, "beep", chan->language);
792 res = ast_waitstream(chan, "");
794 ast_clear_flag(chan, AST_FLAG_SPYING);
797 if (!ast_strlen_zero(exitcontext)) {
801 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
804 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
808 res = ast_waitfordigit(chan, waitms);
810 ast_clear_flag(chan, AST_FLAG_SPYING);
813 if (!ast_strlen_zero(exitcontext)) {
817 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
820 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
823 /* reset for the next loop around, unless overridden later */
827 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
829 chanspy_ds_free(peer_chanspy_ds), prev = peer,
830 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
831 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
834 char *groups[NUM_SPYGROUPS];
835 char *mygroups[NUM_SPYGROUPS];
838 int num_mygroups = 0;
847 int ienf = !myenforced;
849 peer = peer_chanspy_ds->chan;
851 ast_mutex_unlock(&peer_chanspy_ds->lock);
854 ast_channel_unlock(peer);
855 chanspy_ds_free(peer_chanspy_ds);
859 if (ast_check_hangup(chan)) {
860 ast_channel_unlock(peer);
861 chanspy_ds_free(peer_chanspy_ds);
865 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
866 ast_channel_unlock(peer);
870 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
871 ast_channel_unlock(peer);
876 dup_mygroup = ast_strdupa(mygroup);
877 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
878 ARRAY_LEN(mygroups));
880 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
881 ast_copy_string(dup_group, group, sizeof(dup_group));
882 num_groups = ast_app_separate_args(dup_group, ':', groups,
886 for (y = 0; y < num_mygroups; y++) {
887 for (x = 0; x < num_groups; x++) {
888 if (!strcmp(mygroups[y], groups[x])) {
897 ast_channel_unlock(peer);
903 /* We don't need to allocate more space than just the
904 length of (peer->name) for ext as we will cut the
905 channel name's ending before copying into ext */
907 ext = alloca(strlen(peer->name));
909 form_enforced = alloca(strlen(myenforced) + 3);
911 strcpy(form_enforced, ":");
912 strcat(form_enforced, myenforced);
913 strcat(form_enforced, ":");
915 buffer = ast_strdupa(peer->name);
917 if ((end = strchr(buffer, '-'))) {
925 if (strcasestr(form_enforced, ext))
932 strcpy(peer_name, "spy-");
933 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
934 ptr = strchr(peer_name, '/');
936 ptr = strsep(&ptr, "-");
938 for (s = peer_name; s < ptr; s++)
940 /* We have to unlock the peer channel here to avoid a deadlock.
941 * So, when we need to dereference it again, we have to lock the
942 * datastore and get the pointer from there to see if the channel
944 ast_channel_unlock(peer);
946 if (!ast_test_flag(flags, OPTION_QUIET)) {
947 if (ast_test_flag(flags, OPTION_NAME)) {
948 const char *local_context = S_OR(name_context, "default");
949 const char *local_mailbox = S_OR(mailbox, ptr);
950 res = ast_app_sayname(chan, local_mailbox, local_context);
952 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
953 if (!ast_test_flag(flags, OPTION_NOTECH)) {
954 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
955 res = ast_streamfile(chan, peer_name, chan->language);
957 res = ast_waitstream(chan, "");
960 chanspy_ds_free(peer_chanspy_ds);
964 res = ast_say_character_str(chan, peer_name, "", chan->language);
967 if ((num = atoi(ptr)))
968 ast_say_digits(chan, atoi(ptr), "", chan->language);
972 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
976 chanspy_ds_free(peer_chanspy_ds);
978 } else if (res == -2) {
980 chanspy_ds_free(peer_chanspy_ds);
982 } else if (res > 1 && spec) {
983 struct ast_channel *next;
985 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
987 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
988 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
989 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
991 /* stay on this channel, if it is still valid */
993 ast_mutex_lock(&peer_chanspy_ds->lock);
994 if (peer_chanspy_ds->chan) {
995 ast_channel_lock(peer_chanspy_ds->chan);
996 next_chanspy_ds = peer_chanspy_ds;
997 peer_chanspy_ds = NULL;
999 /* the channel is gone */
1000 ast_mutex_unlock(&peer_chanspy_ds->lock);
1001 next_chanspy_ds = NULL;
1008 if (res == -1 || ast_check_hangup(chan))
1013 ast_clear_flag(chan, AST_FLAG_SPYING);
1015 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1017 ast_mutex_lock(&chanspy_ds.lock);
1018 ast_mutex_unlock(&chanspy_ds.lock);
1019 ast_mutex_destroy(&chanspy_ds.lock);
1024 static int chanspy_exec(struct ast_channel *chan, void *data)
1026 char *myenforced = NULL;
1027 char *mygroup = NULL;
1028 char *recbase = NULL;
1030 struct ast_flags flags;
1034 char *mailbox = NULL;
1035 char *name_context = NULL;
1036 AST_DECLARE_APP_ARGS(args,
1038 AST_APP_ARG(options);
1040 char *opts[OPT_ARG_ARRAY_SIZE];
1042 data = ast_strdupa(data);
1043 AST_STANDARD_APP_ARGS(args, data);
1045 if (args.spec && !strcmp(args.spec, "all"))
1049 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1050 if (ast_test_flag(&flags, OPTION_GROUP))
1051 mygroup = opts[OPT_ARG_GROUP];
1053 if (ast_test_flag(&flags, OPTION_RECORD) &&
1054 !(recbase = opts[OPT_ARG_RECORD]))
1055 recbase = "chanspy";
1057 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1060 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1061 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1066 if (ast_test_flag(&flags, OPTION_PRIVATE))
1067 ast_set_flag(&flags, OPTION_WHISPER);
1069 if (ast_test_flag(&flags, OPTION_ENFORCED))
1070 myenforced = opts[OPT_ARG_ENFORCED];
1072 if (ast_test_flag(&flags, OPTION_NAME)) {
1073 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1075 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1076 mailbox = opts[OPT_ARG_NAME];
1077 *delimiter++ = '\0';
1078 name_context = delimiter;
1080 mailbox = opts[OPT_ARG_NAME];
1087 ast_clear_flag(&flags, AST_FLAGS_ALL);
1089 oldwf = chan->writeformat;
1090 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1091 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1096 char filename[PATH_MAX];
1098 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1099 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1100 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1105 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1110 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1111 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1116 static int extenspy_exec(struct ast_channel *chan, void *data)
1118 char *ptr, *exten = NULL;
1119 char *mygroup = NULL;
1120 char *recbase = NULL;
1122 struct ast_flags flags;
1126 char *mailbox = NULL;
1127 char *name_context = NULL;
1128 AST_DECLARE_APP_ARGS(args,
1129 AST_APP_ARG(context);
1130 AST_APP_ARG(options);
1133 data = ast_strdupa(data);
1135 AST_STANDARD_APP_ARGS(args, data);
1136 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1137 exten = args.context;
1142 if (ast_strlen_zero(args.context))
1143 args.context = ast_strdupa(chan->context);
1146 char *opts[OPT_ARG_ARRAY_SIZE];
1148 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1149 if (ast_test_flag(&flags, OPTION_GROUP))
1150 mygroup = opts[OPT_ARG_GROUP];
1152 if (ast_test_flag(&flags, OPTION_RECORD) &&
1153 !(recbase = opts[OPT_ARG_RECORD]))
1154 recbase = "chanspy";
1156 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1159 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1160 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1165 if (ast_test_flag(&flags, OPTION_PRIVATE))
1166 ast_set_flag(&flags, OPTION_WHISPER);
1169 if (ast_test_flag(&flags, OPTION_NAME)) {
1170 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1172 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1173 mailbox = opts[OPT_ARG_NAME];
1174 *delimiter++ = '\0';
1175 name_context = delimiter;
1177 mailbox = opts[OPT_ARG_NAME];
1183 ast_clear_flag(&flags, AST_FLAGS_ALL);
1185 oldwf = chan->writeformat;
1186 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1187 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1192 char filename[PATH_MAX];
1194 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1195 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1196 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1202 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1207 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1208 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1213 static int unload_module(void)
1217 res |= ast_unregister_application(app_chan);
1218 res |= ast_unregister_application(app_ext);
1223 static int load_module(void)
1227 res |= ast_register_application_xml(app_chan, chanspy_exec);
1228 res |= ast_register_application_xml(app_ext, extenspy_exec);
1233 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");