Merged revisions 126573 via svnmerge from
[asterisk-bristuff.git] / apps / app_chanspy.c
blob2f5a997d976b65eaa787fecc1cede78d8041a8f5
1 /*
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
8 * this code.
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.
21 /*! \file
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
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <ctype.h>
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
55 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
56 static const char *app_chan = "ChanSpy";
57 static const char *desc_chan =
58 " ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
59 "audio from an Asterisk channel. This includes the audio coming in and\n"
60 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
61 "only channels beginning with this string will be spied upon.\n"
62 " While spying, the following actions may be performed:\n"
63 " - Dialing # cycles the volume level.\n"
64 " - Dialing * will stop spying and look for another channel to spy on.\n"
65 " - Dialing a series of digits followed by # builds a channel name to append\n"
66 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
67 " the digits '1234#' while spying will begin spying on the channel\n"
68 " 'Agent/1234'. Note that this feature will be overriden if the 'd' option\n"
69 " is used\n"
70 " Note: The X option supersedes the three features above in that if a valid\n"
71 " single digit extension exists in the correct context ChanSpy will\n"
72 " exit to it. This also disables choosing a channel based on 'chanprefix'\n"
73 " and a digit sequence.\n"
74 " Options:\n"
75 " b - Only spy on channels involved in a bridged call.\n"
76 " B - Instead of whispering on a single channel barge in on both\n"
77 " channels involved in the call.\n"
78 " d - Override the typical numeric DTMF functionality and instead\n"
79 " use DTMF to switch between spy modes.\n"
80 " 4 = spy mode\n"
81 " 5 = whisper mode\n"
82 " 6 = barge mode\n"
83 " g(grp) - Only spy on channels in which one or more of the groups \n"
84 " listed in 'grp' matches one or more groups from the\n"
85 " SPYGROUP variable set on the channel to be spied upon.\n"
86 " Note that both 'grp' and SPYGROUP can contain either a\n"
87 " single group or a colon-delimited list of groups, such\n"
88 " as 'sales:support:accounting'.\n"
89 " n([mailbox][@context]) - Say the name of the person being spied on if that person has recorded\n"
90 " his/her name. If a context is specified, then that voicemail context will\n"
91 " be searched when retrieving the name, otherwise the \"default\" context\n"
92 " will be searched. If no mailbox is specified, then the channel name will\n"
93 " be used when searching for the name (i.e. if SIP/1000 is the channel being\n"
94 " spied on and no mailbox is specified, then \"1000\" will be used when searching\n"
95 " for the name).\n"
96 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
97 " selected channel name.\n"
98 " r[(basename)] - Record the session to the monitor spool directory. An\n"
99 " optional base for the filename may be specified. The\n"
100 " default is 'chanspy'.\n"
101 " s - Skip the playback of the channel type (i.e. SIP, IAX, etc) when\n"
102 " speaking the selected channel name.\n"
103 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
104 " negative value refers to a quieter setting.\n"
105 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
106 " the spied-on channel.\n"
107 " W - Enable 'private whisper' mode, so the spying channel can\n"
108 " talk to the spied-on channel but cannot listen to that\n"
109 " channel.\n"
110 " o - Only listen to audio coming from this channel.\n"
111 " X - Allow the user to exit ChanSpy to a valid single digit\n"
112 " numeric extension in the current context or the context\n"
113 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
114 " name of the last channel that was spied on will be stored\n"
115 " in the SPY_CHANNEL variable.\n"
116 " e(ext) - Enable 'enforced' mode, so the spying channel can\n"
117 " only monitor extensions whose name is in the 'ext' : \n"
118 " delimited list.\n"
121 static const char *app_ext = "ExtenSpy";
122 static const char *desc_ext =
123 " ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
124 "audio from an Asterisk channel. This includes the audio coming in and\n"
125 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
126 "specified extension will be selected for spying. If the optional context is not\n"
127 "supplied, the current channel's context will be used.\n"
128 " While spying, the following actions may be performed:\n"
129 " - Dialing # cycles the volume level.\n"
130 " - Dialing * will stop spying and look for another channel to spy on.\n"
131 " Note: The X option superseeds the two features above in that if a valid\n"
132 " single digit extension exists in the correct context it ChanSpy will\n"
133 " exit to it.\n"
134 " Options:\n"
135 " b - Only spy on channels involved in a bridged call.\n"
136 " B - Instead of whispering on a single channel barge in on both\n"
137 " channels involved in the call.\n"
138 " d - Override the typical numeric DTMF functionality and instead\n"
139 " use DTMF to switch between spy modes.\n"
140 " 4 = spy mode\n"
141 " 5 = whisper mode\n"
142 " 6 = barge mode\n"
143 " g(grp) - Only spy on channels in which one or more of the groups \n"
144 " listed in 'grp' matches one or more groups from the\n"
145 " SPYGROUP variable set on the channel to be spied upon.\n"
146 " Note that both 'grp' and SPYGROUP can contain either a\n"
147 " single group or a colon-delimited list of groups, such\n"
148 " as 'sales:support:accounting'.\n"
149 " n([mailbox][@context]) - Say the name of the person being spied on if that person has recorded\n"
150 " his/her name. If a context is specified, then that voicemail context will\n"
151 " be searched when retrieving the name, otherwise the \"default\" context\n"
152 " will be searched. If no mailbox is specified, then the channel name will\n"
153 " be used when searching for the name (i.e. if SIP/1000 is the channel being\n"
154 " spied on and no mailbox is specified, then \"1000\" will be used when searching\n"
155 " for the name).\n"
156 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
157 " selected channel name.\n"
158 " r[(basename)] - Record the session to the monitor spool directory. An\n"
159 " optional base for the filename may be specified. The\n"
160 " default is 'chanspy'.\n"
161 " s - Skip the playback of the channel type (i.e. SIP, IAX, etc) when\n"
162 " speaking the selected channel name.\n"
163 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
164 " negative value refers to a quieter setting.\n"
165 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
166 " the spied-on channel.\n"
167 " W - Enable 'private whisper' mode, so the spying channel can\n"
168 " talk to the spied-on channel but cannot listen to that\n"
169 " channel.\n"
170 " o - Only listen to audio coming from this channel.\n"
171 " X - Allow the user to exit ChanSpy to a valid single digit\n"
172 " numeric extension in the current context or the context\n"
173 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
174 " name of the last channel that was spied on will be stored\n"
175 " in the SPY_CHANNEL variable.\n"
178 enum {
179 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
180 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
181 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
182 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
183 OPTION_RECORD = (1 << 4),
184 OPTION_WHISPER = (1 << 5),
185 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
186 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
187 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
188 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
189 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
190 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
191 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
192 OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */
193 } chanspy_opt_flags;
195 enum {
196 OPT_ARG_VOLUME = 0,
197 OPT_ARG_GROUP,
198 OPT_ARG_RECORD,
199 OPT_ARG_ENFORCED,
200 OPT_ARG_NAME,
201 OPT_ARG_ARRAY_SIZE,
202 } chanspy_opt_args;
204 AST_APP_OPTIONS(spy_opts, {
205 AST_APP_OPTION('q', OPTION_QUIET),
206 AST_APP_OPTION('b', OPTION_BRIDGED),
207 AST_APP_OPTION('B', OPTION_BARGE),
208 AST_APP_OPTION('w', OPTION_WHISPER),
209 AST_APP_OPTION('W', OPTION_PRIVATE),
210 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
211 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
212 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
213 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
214 AST_APP_OPTION('o', OPTION_READONLY),
215 AST_APP_OPTION('X', OPTION_EXIT),
216 AST_APP_OPTION('s', OPTION_NOTECH),
217 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
218 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
221 int next_unique_id_to_use = 0;
223 struct chanspy_translation_helper {
224 /* spy data */
225 struct ast_audiohook spy_audiohook;
226 struct ast_audiohook whisper_audiohook;
227 struct ast_audiohook bridge_whisper_audiohook;
228 int fd;
229 int volfactor;
232 static void *spy_alloc(struct ast_channel *chan, void *data)
234 /* just store the data pointer in the channel structure */
235 return data;
238 static void spy_release(struct ast_channel *chan, void *data)
240 /* nothing to do */
243 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
245 struct chanspy_translation_helper *csth = data;
246 struct ast_frame *f = NULL;
248 ast_audiohook_lock(&csth->spy_audiohook);
249 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
250 /* Channel is already gone more than likely */
251 ast_audiohook_unlock(&csth->spy_audiohook);
252 return -1;
255 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
257 ast_audiohook_unlock(&csth->spy_audiohook);
259 if (!f)
260 return 0;
262 if (ast_write(chan, f)) {
263 ast_frfree(f);
264 return -1;
267 if (csth->fd)
268 write(csth->fd, f->data.ptr, f->datalen);
270 ast_frfree(f);
272 return 0;
275 static struct ast_generator spygen = {
276 .alloc = spy_alloc,
277 .release = spy_release,
278 .generate = spy_generate,
281 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
283 int res = 0;
284 struct ast_channel *peer = NULL;
286 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
288 res = ast_audiohook_attach(chan, audiohook);
290 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
291 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
293 return res;
296 struct chanspy_ds {
297 struct ast_channel *chan;
298 char unique_id[20];
299 ast_mutex_t lock;
302 static void change_spy_mode(const char digit, struct ast_flags *flags)
304 if (digit == '4') {
305 ast_clear_flag(flags, OPTION_WHISPER);
306 ast_clear_flag(flags, OPTION_BARGE);
307 } else if (digit == '5') {
308 ast_clear_flag(flags, OPTION_BARGE);
309 ast_set_flag(flags, OPTION_WHISPER);
310 } else if (digit == '6') {
311 ast_clear_flag(flags, OPTION_WHISPER);
312 ast_set_flag(flags, OPTION_BARGE);
316 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
317 int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
319 struct chanspy_translation_helper csth;
320 int running = 0, res, x = 0;
321 char inp[24] = {0};
322 char *name;
323 struct ast_frame *f;
324 struct ast_silence_generator *silgen = NULL;
325 struct ast_channel *spyee = NULL;
326 const char *spyer_name;
328 ast_channel_lock(chan);
329 spyer_name = ast_strdupa(chan->name);
330 ast_channel_unlock(chan);
332 ast_mutex_lock(&spyee_chanspy_ds->lock);
333 if (spyee_chanspy_ds->chan) {
334 spyee = spyee_chanspy_ds->chan;
335 ast_channel_lock(spyee);
337 ast_mutex_unlock(&spyee_chanspy_ds->lock);
339 if (!spyee)
340 return 0;
342 /* We now hold the channel lock on spyee */
344 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
345 ast_channel_unlock(spyee);
346 return 0;
349 name = ast_strdupa(spyee->name);
350 ast_verb(2, "Spying on channel %s\n", name);
352 memset(&csth, 0, sizeof(csth));
354 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
356 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
357 ast_audiohook_destroy(&csth.spy_audiohook);
358 ast_channel_unlock(spyee);
359 return 0;
362 ast_channel_lock(chan);
363 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
364 ast_channel_unlock(chan);
366 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
367 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
368 start_spying(spyee, spyer_name, &csth.whisper_audiohook); /* Unlocks spyee */
369 start_spying(ast_bridged_channel(spyee), spyer_name, &csth.bridge_whisper_audiohook);
371 ast_channel_unlock(spyee);
372 spyee = NULL;
374 csth.volfactor = *volfactor;
376 if (csth.volfactor) {
377 csth.spy_audiohook.options.read_volume = csth.volfactor;
378 csth.spy_audiohook.options.write_volume = csth.volfactor;
381 csth.fd = fd;
383 if (ast_test_flag(flags, OPTION_PRIVATE))
384 silgen = ast_channel_start_silence_generator(chan);
385 else
386 ast_activate_generator(chan, &spygen, &csth);
388 /* We can no longer rely on 'spyee' being an actual channel;
389 it can be hung up and freed out from under us. However, the
390 channel destructor will put NULL into our csth.spy.chan
391 field when that happens, so that is our signal that the spyee
392 channel has gone away.
395 /* Note: it is very important that the ast_waitfor() be the first
396 condition in this expression, so that if we wait for some period
397 of time before receiving a frame from our spying channel, we check
398 for hangup on the spied-on channel _after_ knowing that a frame
399 has arrived, since the spied-on channel could have gone away while
400 we were waiting
402 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
403 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
404 running = -1;
405 break;
408 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
409 ast_audiohook_lock(&csth.whisper_audiohook);
410 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
411 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
412 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
413 ast_audiohook_unlock(&csth.whisper_audiohook);
414 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
415 ast_frfree(f);
416 continue;
417 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
418 ast_audiohook_lock(&csth.whisper_audiohook);
419 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
420 ast_audiohook_unlock(&csth.whisper_audiohook);
421 ast_frfree(f);
422 continue;
425 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
426 ast_frfree(f);
427 if (!res)
428 continue;
430 if (x == sizeof(inp))
431 x = 0;
433 if (res < 0) {
434 running = -1;
435 break;
438 if (ast_test_flag(flags, OPTION_EXIT)) {
439 char tmp[2];
440 tmp[0] = res;
441 tmp[1] = '\0';
442 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
443 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
444 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
445 running = -2;
446 break;
447 } else {
448 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
450 } else if (res >= '0' && res <= '9') {
451 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
452 change_spy_mode(res, flags);
453 } else {
454 inp[x++] = res;
458 if (res == '*') {
459 running = 0;
460 break;
461 } else if (res == '#') {
462 if (!ast_strlen_zero(inp)) {
463 running = atoi(inp);
464 break;
467 (*volfactor)++;
468 if (*volfactor > 4)
469 *volfactor = -4;
470 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
472 csth.volfactor = *volfactor;
473 csth.spy_audiohook.options.read_volume = csth.volfactor;
474 csth.spy_audiohook.options.write_volume = csth.volfactor;
478 if (ast_test_flag(flags, OPTION_PRIVATE))
479 ast_channel_stop_silence_generator(chan, silgen);
480 else
481 ast_deactivate_generator(chan);
483 ast_channel_lock(chan);
484 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
485 ast_channel_unlock(chan);
487 ast_audiohook_lock(&csth.whisper_audiohook);
488 ast_audiohook_detach(&csth.whisper_audiohook);
489 ast_audiohook_unlock(&csth.whisper_audiohook);
490 ast_audiohook_destroy(&csth.whisper_audiohook);
491 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
492 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
493 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
494 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
496 ast_audiohook_lock(&csth.spy_audiohook);
497 ast_audiohook_detach(&csth.spy_audiohook);
498 ast_audiohook_unlock(&csth.spy_audiohook);
499 ast_audiohook_destroy(&csth.spy_audiohook);
501 ast_verb(2, "Done Spying on channel %s\n", name);
503 return running;
507 * \note This relies on the embedded lock to be recursive, as it may be called
508 * due to a call to chanspy_ds_free with the lock held there.
510 static void chanspy_ds_destroy(void *data)
512 struct chanspy_ds *chanspy_ds = data;
514 /* Setting chan to be NULL is an atomic operation, but we don't want this
515 * value to change while this lock is held. The lock is held elsewhere
516 * while it performs non-atomic operations with this channel pointer */
518 ast_mutex_lock(&chanspy_ds->lock);
519 chanspy_ds->chan = NULL;
520 ast_mutex_unlock(&chanspy_ds->lock);
523 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
525 struct chanspy_ds *chanspy_ds = data;
527 ast_mutex_lock(&chanspy_ds->lock);
528 chanspy_ds->chan = new_chan;
529 ast_mutex_unlock(&chanspy_ds->lock);
532 static const struct ast_datastore_info chanspy_ds_info = {
533 .type = "chanspy",
534 .destroy = chanspy_ds_destroy,
535 .chan_fixup = chanspy_ds_chan_fixup,
538 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
540 if (!chanspy_ds)
541 return NULL;
543 ast_mutex_lock(&chanspy_ds->lock);
544 if (chanspy_ds->chan) {
545 struct ast_datastore *datastore;
546 struct ast_channel *chan;
548 chan = chanspy_ds->chan;
550 ast_channel_lock(chan);
551 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
552 ast_channel_datastore_remove(chan, datastore);
553 /* chanspy_ds->chan is NULL after this call */
554 chanspy_ds_destroy(datastore->data);
555 datastore->data = NULL;
556 ast_channel_datastore_free(datastore);
558 ast_channel_unlock(chan);
560 ast_mutex_unlock(&chanspy_ds->lock);
562 return NULL;
565 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
566 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
568 struct ast_datastore *datastore = NULL;
570 ast_mutex_lock(&chanspy_ds->lock);
572 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
573 ast_mutex_unlock(&chanspy_ds->lock);
574 chanspy_ds = chanspy_ds_free(chanspy_ds);
575 ast_channel_unlock(chan);
576 return NULL;
579 chanspy_ds->chan = chan;
580 datastore->data = chanspy_ds;
581 ast_channel_datastore_add(chan, datastore);
583 return chanspy_ds;
586 static struct chanspy_ds *next_channel(struct ast_channel *chan,
587 const struct ast_channel *last, const char *spec,
588 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
590 struct ast_channel *next;
591 char channel_name[AST_CHANNEL_NAME];
593 redo:
594 if (!ast_strlen_zero(spec))
595 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
596 else if (!ast_strlen_zero(exten))
597 next = ast_walk_channel_by_exten_locked(last, exten, context);
598 else
599 next = ast_channel_walk_locked(last);
601 if (!next)
602 return NULL;
604 snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name);
605 if (!strncmp(next->name, channel_name, 10)) {
606 ast_channel_unlock(next);
607 goto redo;
608 } else if (next == chan) {
609 last = next;
610 ast_channel_unlock(next);
611 goto redo;
614 return setup_chanspy_ds(next, chanspy_ds);
617 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
618 int volfactor, const int fd, const char *mygroup, const char *myenforced,
619 const char *spec, const char *exten, const char *context, const char *mailbox,
620 const char *name_context)
622 char nameprefix[AST_NAME_STRLEN];
623 char peer_name[AST_NAME_STRLEN + 5];
624 char exitcontext[AST_MAX_CONTEXT] = "";
625 signed char zero_volume = 0;
626 int waitms;
627 int res;
628 char *ptr;
629 int num;
630 int num_spyed_upon = 1;
631 struct chanspy_ds chanspy_ds;
633 if (ast_test_flag(flags, OPTION_EXIT)) {
634 const char *c;
635 ast_channel_lock(chan);
636 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
637 ast_copy_string(exitcontext, c, sizeof(exitcontext));
638 } else if (!ast_strlen_zero(chan->macrocontext)) {
639 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
640 } else {
641 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
643 ast_channel_unlock(chan);
646 ast_mutex_init(&chanspy_ds.lock);
648 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
650 if (chan->_state != AST_STATE_UP)
651 ast_answer(chan);
653 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
655 waitms = 100;
657 for (;;) {
658 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
659 struct ast_channel *prev = NULL, *peer = NULL;
661 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
662 res = ast_streamfile(chan, "beep", chan->language);
663 if (!res)
664 res = ast_waitstream(chan, "");
665 else if (res < 0) {
666 ast_clear_flag(chan, AST_FLAG_SPYING);
667 break;
669 if (!ast_strlen_zero(exitcontext)) {
670 char tmp[2];
671 tmp[0] = res;
672 tmp[1] = '\0';
673 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
674 goto exit;
675 else
676 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
680 res = ast_waitfordigit(chan, waitms);
681 if (res < 0) {
682 ast_clear_flag(chan, AST_FLAG_SPYING);
683 break;
685 if (!ast_strlen_zero(exitcontext)) {
686 char tmp[2];
687 tmp[0] = res;
688 tmp[1] = '\0';
689 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
690 goto exit;
691 else
692 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
695 /* reset for the next loop around, unless overridden later */
696 waitms = 100;
697 num_spyed_upon = 0;
699 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
700 peer_chanspy_ds;
701 chanspy_ds_free(peer_chanspy_ds), prev = peer,
702 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
703 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
704 const char *group;
705 int igrp = !mygroup;
706 char *groups[NUM_SPYGROUPS];
707 char *mygroups[NUM_SPYGROUPS];
708 int num_groups = 0;
709 char *dup_group;
710 int num_mygroups = 0;
711 char *dup_mygroup;
712 int x;
713 int y;
714 char *s;
715 char *buffer;
716 char *end;
717 char *ext;
718 char *form_enforced;
719 int ienf = !myenforced;
721 peer = peer_chanspy_ds->chan;
723 ast_mutex_unlock(&peer_chanspy_ds->lock);
725 if (peer == prev) {
726 ast_channel_unlock(peer);
727 chanspy_ds_free(peer_chanspy_ds);
728 break;
731 if (ast_check_hangup(chan)) {
732 ast_channel_unlock(peer);
733 chanspy_ds_free(peer_chanspy_ds);
734 break;
737 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
738 ast_channel_unlock(peer);
739 continue;
742 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
743 ast_channel_unlock(peer);
744 continue;
747 if (mygroup) {
748 dup_mygroup = ast_strdupa(mygroup);
749 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
750 sizeof(mygroups) / sizeof(mygroups[0]));
752 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
753 dup_group = ast_strdupa(group);
754 num_groups = ast_app_separate_args(dup_group, ':', groups,
755 sizeof(groups) / sizeof(groups[0]));
758 for (y = 0; y < num_mygroups; y++) {
759 for (x = 0; x < num_groups; x++) {
760 if (!strcmp(mygroups[y], groups[x])) {
761 igrp = 1;
762 break;
768 if (!igrp) {
769 ast_channel_unlock(peer);
770 continue;
773 if (myenforced) {
775 /* We don't need to allocate more space than just the
776 length of (peer->name) for ext as we will cut the
777 channel name's ending before copying into ext */
779 ext = alloca(strlen(peer->name));
781 form_enforced = alloca(strlen(myenforced) + 3);
783 strcpy(form_enforced, ":");
784 strcat(form_enforced, myenforced);
785 strcat(form_enforced, ":");
787 buffer = ast_strdupa(peer->name);
789 if ((end = strchr(buffer, '-'))) {
790 *end++ = ':';
791 *end = '\0';
794 strcpy(ext, ":");
795 strcat(ext, buffer);
797 if (strcasestr(form_enforced, ext))
798 ienf = 1;
801 if (!ienf)
802 continue;
804 strcpy(peer_name, "spy-");
805 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
806 ptr = strchr(peer_name, '/');
807 *ptr++ = '\0';
808 ptr = strsep(&ptr, "-");
810 for (s = peer_name; s < ptr; s++)
811 *s = tolower(*s);
812 /* We have to unlock the peer channel here to avoid a deadlock.
813 * So, when we need to dereference it again, we have to lock the
814 * datastore and get the pointer from there to see if the channel
815 * is still valid. */
816 ast_channel_unlock(peer);
818 if (!ast_test_flag(flags, OPTION_QUIET)) {
819 if (ast_test_flag(flags, OPTION_NAME)) {
820 const char *local_context = S_OR(name_context, "default");
821 const char *local_mailbox = S_OR(mailbox, ptr);
822 res = ast_app_sayname(chan, local_mailbox, local_context);
824 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
825 if (!ast_test_flag(flags, OPTION_NOTECH)) {
826 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
827 res = ast_streamfile(chan, peer_name, chan->language);
828 if (!res) {
829 res = ast_waitstream(chan, "");
831 if (res) {
832 chanspy_ds_free(peer_chanspy_ds);
833 break;
835 } else {
836 res = ast_say_character_str(chan, peer_name, "", chan->language);
839 if ((num = atoi(ptr)))
840 ast_say_digits(chan, atoi(ptr), "", chan->language);
844 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
845 num_spyed_upon++;
847 if (res == -1) {
848 chanspy_ds_free(peer_chanspy_ds);
849 goto exit;
850 } else if (res == -2) {
851 res = 0;
852 chanspy_ds_free(peer_chanspy_ds);
853 goto exit;
854 } else if (res > 1 && spec) {
855 struct ast_channel *next;
857 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
859 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
860 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
861 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
862 } else {
863 /* stay on this channel, if it is still valid */
865 ast_mutex_lock(&peer_chanspy_ds->lock);
866 if (peer_chanspy_ds->chan) {
867 ast_channel_lock(peer_chanspy_ds->chan);
868 next_chanspy_ds = peer_chanspy_ds;
869 peer_chanspy_ds = NULL;
870 } else {
871 /* the channel is gone */
872 ast_mutex_unlock(&peer_chanspy_ds->lock);
873 next_chanspy_ds = NULL;
877 peer = NULL;
880 if (res == -1 || ast_check_hangup(chan))
881 break;
883 exit:
885 ast_clear_flag(chan, AST_FLAG_SPYING);
887 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
889 ast_mutex_destroy(&chanspy_ds.lock);
891 return res;
894 static int chanspy_exec(struct ast_channel *chan, void *data)
896 char *myenforced = NULL;
897 char *mygroup = NULL;
898 char *recbase = NULL;
899 int fd = 0;
900 struct ast_flags flags;
901 int oldwf = 0;
902 int volfactor = 0;
903 int res;
904 char *mailbox = NULL;
905 char *name_context = NULL;
906 AST_DECLARE_APP_ARGS(args,
907 AST_APP_ARG(spec);
908 AST_APP_ARG(options);
910 char *opts[OPT_ARG_ARRAY_SIZE];
912 data = ast_strdupa(data);
913 AST_STANDARD_APP_ARGS(args, data);
915 if (args.spec && !strcmp(args.spec, "all"))
916 args.spec = NULL;
918 if (args.options) {
919 ast_app_parse_options(spy_opts, &flags, opts, args.options);
920 if (ast_test_flag(&flags, OPTION_GROUP))
921 mygroup = opts[OPT_ARG_GROUP];
923 if (ast_test_flag(&flags, OPTION_RECORD) &&
924 !(recbase = opts[OPT_ARG_RECORD]))
925 recbase = "chanspy";
927 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
928 int vol;
930 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
931 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
932 else
933 volfactor = vol;
936 if (ast_test_flag(&flags, OPTION_PRIVATE))
937 ast_set_flag(&flags, OPTION_WHISPER);
939 if (ast_test_flag(&flags, OPTION_ENFORCED))
940 myenforced = opts[OPT_ARG_ENFORCED];
942 if (ast_test_flag(&flags, OPTION_NAME)) {
943 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
944 char *delimiter;
945 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
946 mailbox = opts[OPT_ARG_NAME];
947 *delimiter++ = '\0';
948 name_context = delimiter;
949 } else {
950 mailbox = opts[OPT_ARG_NAME];
956 } else
957 ast_clear_flag(&flags, AST_FLAGS_ALL);
959 oldwf = chan->writeformat;
960 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
961 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
962 return -1;
965 if (recbase) {
966 char filename[PATH_MAX];
968 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
969 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
970 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
971 fd = 0;
975 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
977 if (fd)
978 close(fd);
980 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
981 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
983 return res;
986 static int extenspy_exec(struct ast_channel *chan, void *data)
988 char *ptr, *exten = NULL;
989 char *mygroup = NULL;
990 char *recbase = NULL;
991 int fd = 0;
992 struct ast_flags flags;
993 int oldwf = 0;
994 int volfactor = 0;
995 int res;
996 char *mailbox = NULL;
997 char *name_context = NULL;
998 AST_DECLARE_APP_ARGS(args,
999 AST_APP_ARG(context);
1000 AST_APP_ARG(options);
1003 data = ast_strdupa(data);
1005 AST_STANDARD_APP_ARGS(args, data);
1006 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1007 exten = args.context;
1008 *ptr++ = '\0';
1009 args.context = ptr;
1012 if (ast_strlen_zero(args.context))
1013 args.context = ast_strdupa(chan->context);
1015 if (args.options) {
1016 char *opts[OPT_ARG_ARRAY_SIZE];
1018 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1019 if (ast_test_flag(&flags, OPTION_GROUP))
1020 mygroup = opts[OPT_ARG_GROUP];
1022 if (ast_test_flag(&flags, OPTION_RECORD) &&
1023 !(recbase = opts[OPT_ARG_RECORD]))
1024 recbase = "chanspy";
1026 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1027 int vol;
1029 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1030 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1031 else
1032 volfactor = vol;
1035 if (ast_test_flag(&flags, OPTION_PRIVATE))
1036 ast_set_flag(&flags, OPTION_WHISPER);
1039 if (ast_test_flag(&flags, OPTION_NAME)) {
1040 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1041 char *delimiter;
1042 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1043 mailbox = opts[OPT_ARG_NAME];
1044 *delimiter++ = '\0';
1045 name_context = delimiter;
1046 } else {
1047 mailbox = opts[OPT_ARG_NAME];
1052 } else
1053 ast_clear_flag(&flags, AST_FLAGS_ALL);
1055 oldwf = chan->writeformat;
1056 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1057 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1058 return -1;
1061 if (recbase) {
1062 char filename[PATH_MAX];
1064 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1065 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1066 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1067 fd = 0;
1072 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1074 if (fd)
1075 close(fd);
1077 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1078 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1080 return res;
1083 static int unload_module(void)
1085 int res = 0;
1087 res |= ast_unregister_application(app_chan);
1088 res |= ast_unregister_application(app_ext);
1090 return res;
1093 static int load_module(void)
1095 int res = 0;
1097 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
1098 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
1100 return res;
1103 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");