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 if (ast_test_flag(flags, OPTION_WHISPER)) {
615 ast_audiohook_lock(&csth.whisper_audiohook);
616 ast_audiohook_detach(&csth.whisper_audiohook);
617 ast_audiohook_unlock(&csth.whisper_audiohook);
618 ast_audiohook_destroy(&csth.whisper_audiohook);
621 if (ast_test_flag(flags, OPTION_BARGE)) {
622 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
623 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
624 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
625 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
628 ast_audiohook_lock(&csth.spy_audiohook);
629 ast_audiohook_detach(&csth.spy_audiohook);
630 ast_audiohook_unlock(&csth.spy_audiohook);
631 ast_audiohook_destroy(&csth.spy_audiohook);
633 ast_verb(2, "Done Spying on channel %s\n", name);
639 * \note This relies on the embedded lock to be recursive, as it may be called
640 * due to a call to chanspy_ds_free with the lock held there.
642 static void chanspy_ds_destroy(void *data)
644 struct chanspy_ds *chanspy_ds = data;
646 /* Setting chan to be NULL is an atomic operation, but we don't want this
647 * value to change while this lock is held. The lock is held elsewhere
648 * while it performs non-atomic operations with this channel pointer */
650 ast_mutex_lock(&chanspy_ds->lock);
651 chanspy_ds->chan = NULL;
652 ast_mutex_unlock(&chanspy_ds->lock);
655 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
657 struct chanspy_ds *chanspy_ds = data;
659 ast_mutex_lock(&chanspy_ds->lock);
660 chanspy_ds->chan = new_chan;
661 ast_mutex_unlock(&chanspy_ds->lock);
664 static const struct ast_datastore_info chanspy_ds_info = {
666 .destroy = chanspy_ds_destroy,
667 .chan_fixup = chanspy_ds_chan_fixup,
670 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
675 ast_mutex_lock(&chanspy_ds->lock);
676 if (chanspy_ds->chan) {
677 struct ast_datastore *datastore;
678 struct ast_channel *chan;
680 chan = chanspy_ds->chan;
682 ast_channel_lock(chan);
683 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
684 ast_channel_datastore_remove(chan, datastore);
685 /* chanspy_ds->chan is NULL after this call */
686 chanspy_ds_destroy(datastore->data);
687 datastore->data = NULL;
688 ast_datastore_free(datastore);
690 ast_channel_unlock(chan);
692 ast_mutex_unlock(&chanspy_ds->lock);
697 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
698 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
700 struct ast_datastore *datastore = NULL;
702 ast_mutex_lock(&chanspy_ds->lock);
704 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
705 ast_mutex_unlock(&chanspy_ds->lock);
706 chanspy_ds = chanspy_ds_free(chanspy_ds);
707 ast_channel_unlock(chan);
711 chanspy_ds->chan = chan;
712 datastore->data = chanspy_ds;
713 ast_channel_datastore_add(chan, datastore);
718 static struct chanspy_ds *next_channel(struct ast_channel *chan,
719 const struct ast_channel *last, const char *spec,
720 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
722 struct ast_channel *next;
723 const size_t pseudo_len = strlen("DAHDI/pseudo");
726 if (!ast_strlen_zero(spec))
727 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
728 else if (!ast_strlen_zero(exten))
729 next = ast_walk_channel_by_exten_locked(last, exten, context);
731 next = ast_channel_walk_locked(last);
736 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
738 ast_channel_unlock(next);
740 } else if (next == chan) {
742 ast_channel_unlock(next);
746 return setup_chanspy_ds(next, chanspy_ds);
749 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
750 int volfactor, const int fd, const char *mygroup, const char *myenforced,
751 const char *spec, const char *exten, const char *context, const char *mailbox,
752 const char *name_context)
754 char nameprefix[AST_NAME_STRLEN];
755 char peer_name[AST_NAME_STRLEN + 5];
756 char exitcontext[AST_MAX_CONTEXT] = "";
757 signed char zero_volume = 0;
762 int num_spyed_upon = 1;
763 struct chanspy_ds chanspy_ds = { 0, };
765 if (ast_test_flag(flags, OPTION_EXIT)) {
767 ast_channel_lock(chan);
768 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
769 ast_copy_string(exitcontext, c, sizeof(exitcontext));
770 } else if (!ast_strlen_zero(chan->macrocontext)) {
771 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
773 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
775 ast_channel_unlock(chan);
778 ast_mutex_init(&chanspy_ds.lock);
780 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
782 if (chan->_state != AST_STATE_UP)
785 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
790 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
791 struct ast_channel *prev = NULL, *peer = NULL;
793 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
794 res = ast_streamfile(chan, "beep", chan->language);
796 res = ast_waitstream(chan, "");
798 ast_clear_flag(chan, AST_FLAG_SPYING);
801 if (!ast_strlen_zero(exitcontext)) {
805 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
808 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
812 res = ast_waitfordigit(chan, waitms);
814 ast_clear_flag(chan, AST_FLAG_SPYING);
817 if (!ast_strlen_zero(exitcontext)) {
821 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
824 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
827 /* reset for the next loop around, unless overridden later */
831 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
833 chanspy_ds_free(peer_chanspy_ds), prev = peer,
834 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
835 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
838 char *groups[NUM_SPYGROUPS];
839 char *mygroups[NUM_SPYGROUPS];
842 int num_mygroups = 0;
851 int ienf = !myenforced;
853 peer = peer_chanspy_ds->chan;
855 ast_mutex_unlock(&peer_chanspy_ds->lock);
858 ast_channel_unlock(peer);
859 chanspy_ds_free(peer_chanspy_ds);
863 if (ast_check_hangup(chan)) {
864 ast_channel_unlock(peer);
865 chanspy_ds_free(peer_chanspy_ds);
869 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
870 ast_channel_unlock(peer);
874 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
875 ast_channel_unlock(peer);
880 dup_mygroup = ast_strdupa(mygroup);
881 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
882 ARRAY_LEN(mygroups));
884 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
885 ast_copy_string(dup_group, group, sizeof(dup_group));
886 num_groups = ast_app_separate_args(dup_group, ':', groups,
890 for (y = 0; y < num_mygroups; y++) {
891 for (x = 0; x < num_groups; x++) {
892 if (!strcmp(mygroups[y], groups[x])) {
901 ast_channel_unlock(peer);
907 /* We don't need to allocate more space than just the
908 length of (peer->name) for ext as we will cut the
909 channel name's ending before copying into ext */
911 ext = alloca(strlen(peer->name));
913 form_enforced = alloca(strlen(myenforced) + 3);
915 strcpy(form_enforced, ":");
916 strcat(form_enforced, myenforced);
917 strcat(form_enforced, ":");
919 buffer = ast_strdupa(peer->name);
921 if ((end = strchr(buffer, '-'))) {
929 if (strcasestr(form_enforced, ext))
936 strcpy(peer_name, "spy-");
937 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
938 ptr = strchr(peer_name, '/');
940 ptr = strsep(&ptr, "-");
942 for (s = peer_name; s < ptr; s++)
944 /* We have to unlock the peer channel here to avoid a deadlock.
945 * So, when we need to dereference it again, we have to lock the
946 * datastore and get the pointer from there to see if the channel
948 ast_channel_unlock(peer);
950 if (!ast_test_flag(flags, OPTION_QUIET)) {
951 if (ast_test_flag(flags, OPTION_NAME)) {
952 const char *local_context = S_OR(name_context, "default");
953 const char *local_mailbox = S_OR(mailbox, ptr);
954 res = ast_app_sayname(chan, local_mailbox, local_context);
956 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
957 if (!ast_test_flag(flags, OPTION_NOTECH)) {
958 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
959 res = ast_streamfile(chan, peer_name, chan->language);
961 res = ast_waitstream(chan, "");
964 chanspy_ds_free(peer_chanspy_ds);
968 res = ast_say_character_str(chan, peer_name, "", chan->language);
971 if ((num = atoi(ptr)))
972 ast_say_digits(chan, atoi(ptr), "", chan->language);
976 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
980 chanspy_ds_free(peer_chanspy_ds);
982 } else if (res == -2) {
984 chanspy_ds_free(peer_chanspy_ds);
986 } else if (res > 1 && spec) {
987 struct ast_channel *next;
989 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
991 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
992 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
993 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
995 /* stay on this channel, if it is still valid */
997 ast_mutex_lock(&peer_chanspy_ds->lock);
998 if (peer_chanspy_ds->chan) {
999 ast_channel_lock(peer_chanspy_ds->chan);
1000 next_chanspy_ds = peer_chanspy_ds;
1001 peer_chanspy_ds = NULL;
1003 /* the channel is gone */
1004 ast_mutex_unlock(&peer_chanspy_ds->lock);
1005 next_chanspy_ds = NULL;
1012 if (res == -1 || ast_check_hangup(chan))
1017 ast_clear_flag(chan, AST_FLAG_SPYING);
1019 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1021 ast_mutex_lock(&chanspy_ds.lock);
1022 ast_mutex_unlock(&chanspy_ds.lock);
1023 ast_mutex_destroy(&chanspy_ds.lock);
1028 static int chanspy_exec(struct ast_channel *chan, void *data)
1030 char *myenforced = NULL;
1031 char *mygroup = NULL;
1032 char *recbase = NULL;
1034 struct ast_flags flags;
1038 char *mailbox = NULL;
1039 char *name_context = NULL;
1040 AST_DECLARE_APP_ARGS(args,
1042 AST_APP_ARG(options);
1044 char *opts[OPT_ARG_ARRAY_SIZE];
1046 data = ast_strdupa(data);
1047 AST_STANDARD_APP_ARGS(args, data);
1049 if (args.spec && !strcmp(args.spec, "all"))
1053 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1054 if (ast_test_flag(&flags, OPTION_GROUP))
1055 mygroup = opts[OPT_ARG_GROUP];
1057 if (ast_test_flag(&flags, OPTION_RECORD) &&
1058 !(recbase = opts[OPT_ARG_RECORD]))
1059 recbase = "chanspy";
1061 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1064 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1065 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1070 if (ast_test_flag(&flags, OPTION_PRIVATE))
1071 ast_set_flag(&flags, OPTION_WHISPER);
1073 if (ast_test_flag(&flags, OPTION_ENFORCED))
1074 myenforced = opts[OPT_ARG_ENFORCED];
1076 if (ast_test_flag(&flags, OPTION_NAME)) {
1077 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1079 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1080 mailbox = opts[OPT_ARG_NAME];
1081 *delimiter++ = '\0';
1082 name_context = delimiter;
1084 mailbox = opts[OPT_ARG_NAME];
1091 ast_clear_flag(&flags, AST_FLAGS_ALL);
1093 oldwf = chan->writeformat;
1094 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1095 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1100 char filename[PATH_MAX];
1102 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1103 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1104 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1109 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1114 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1115 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1120 static int extenspy_exec(struct ast_channel *chan, void *data)
1122 char *ptr, *exten = NULL;
1123 char *mygroup = NULL;
1124 char *recbase = NULL;
1126 struct ast_flags flags;
1130 char *mailbox = NULL;
1131 char *name_context = NULL;
1132 AST_DECLARE_APP_ARGS(args,
1133 AST_APP_ARG(context);
1134 AST_APP_ARG(options);
1137 data = ast_strdupa(data);
1139 AST_STANDARD_APP_ARGS(args, data);
1140 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1141 exten = args.context;
1146 if (ast_strlen_zero(args.context))
1147 args.context = ast_strdupa(chan->context);
1150 char *opts[OPT_ARG_ARRAY_SIZE];
1152 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1153 if (ast_test_flag(&flags, OPTION_GROUP))
1154 mygroup = opts[OPT_ARG_GROUP];
1156 if (ast_test_flag(&flags, OPTION_RECORD) &&
1157 !(recbase = opts[OPT_ARG_RECORD]))
1158 recbase = "chanspy";
1160 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1163 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1164 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1169 if (ast_test_flag(&flags, OPTION_PRIVATE))
1170 ast_set_flag(&flags, OPTION_WHISPER);
1173 if (ast_test_flag(&flags, OPTION_NAME)) {
1174 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1176 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1177 mailbox = opts[OPT_ARG_NAME];
1178 *delimiter++ = '\0';
1179 name_context = delimiter;
1181 mailbox = opts[OPT_ARG_NAME];
1187 ast_clear_flag(&flags, AST_FLAGS_ALL);
1189 oldwf = chan->writeformat;
1190 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1191 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1196 char filename[PATH_MAX];
1198 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1199 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1200 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1206 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1211 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1212 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1217 static int unload_module(void)
1221 res |= ast_unregister_application(app_chan);
1222 res |= ast_unregister_application(app_ext);
1227 static int load_module(void)
1231 res |= ast_register_application_xml(app_chan, chanspy_exec);
1232 res |= ast_register_application_xml(app_ext, extenspy_exec);
1237 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");