2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2008, Digium, Inc.
7 * A license has been granted to Digium (via disclaimer) for the use of
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
23 * \brief ChanSpy: Listen in on any channel.
25 * \author Anthony Minessale II <anthmct@yahoo.com>
26 * \author Joshua Colp <jcolp@digium.com>
27 * \author Russell Bryant <russell@digium.com>
29 * \ingroup applications
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/audiohook.h"
42 #include "asterisk/features.h"
43 #include "asterisk/app.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/say.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/translate.h"
48 #include "asterisk/module.h"
49 #include "asterisk/lock.h"
51 #define AST_NAME_STRLEN 256
53 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
54 static const char *app_chan = "ChanSpy";
55 static const char *desc_chan =
56 " ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
57 "audio from an Asterisk channel. This includes the audio coming in and\n"
58 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
59 "only channels beginning with this string will be spied upon.\n"
60 " While spying, the following actions may be performed:\n"
61 " - Dialing # cycles the volume level.\n"
62 " - Dialing * will stop spying and look for another channel to spy on.\n"
63 " - Dialing a series of digits followed by # builds a channel name to append\n"
64 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
65 " the digits '1234#' while spying will begin spying on the channel\n"
67 " Note: The X option supersedes the three features above in that if a valid\n"
68 " single digit extension exists in the correct context ChanSpy will\n"
69 " exit to it. This also disables choosing a channel based on 'chanprefix'\n"
70 " and a digit sequence.\n"
72 " b - Only spy on channels involved in a bridged call.\n"
73 " g(grp) - Match only channels where their SPYGROUP variable is set to\n"
74 " contain 'grp' in an optional : delimited list.\n"
75 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
76 " selected channel name.\n"
77 " r[(basename)] - Record the session to the monitor spool directory. An\n"
78 " optional base for the filename may be specified. The\n"
79 " default is 'chanspy'.\n"
80 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
81 " negative value refers to a quieter setting.\n"
82 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
83 " the spied-on channel.\n"
84 " W - Enable 'private whisper' mode, so the spying channel can\n"
85 " talk to the spied-on channel but cannot listen to that\n"
87 " o - Only listen to audio coming from this channel.\n"
88 " X - Allow the user to exit ChanSpy to a valid single digit\n"
89 " numeric extension in the current context or the context\n"
90 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
91 " name of the last channel that was spied on will be stored\n"
92 " in the SPY_CHANNEL variable.\n"
93 " e(ext) - Enable 'enforced' mode, so the spying channel can\n"
94 " only monitor extensions whose name is in the 'ext' : \n"
98 static const char *app_ext = "ExtenSpy";
99 static const char *desc_ext =
100 " ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
101 "audio from an Asterisk channel. This includes the audio coming in and\n"
102 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
103 "specified extension will be selected for spying. If the optional context is not\n"
104 "supplied, the current channel's context will be used.\n"
105 " While spying, the following actions may be performed:\n"
106 " - Dialing # cycles the volume level.\n"
107 " - Dialing * will stop spying and look for another channel to spy on.\n"
108 " Note: The X option superseeds the two features above in that if a valid\n"
109 " single digit extension exists in the correct context it ChanSpy will\n"
112 " b - Only spy on channels involved in a bridged call.\n"
113 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
114 " contain 'grp' in an optional : delimited list.\n"
115 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
116 " selected channel name.\n"
117 " r[(basename)] - Record the session to the monitor spool directory. An\n"
118 " optional base for the filename may be specified. The\n"
119 " default is 'chanspy'.\n"
120 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
121 " negative value refers to a quieter setting.\n"
122 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
123 " the spied-on channel.\n"
124 " W - Enable 'private whisper' mode, so the spying channel can\n"
125 " talk to the spied-on channel but cannot listen to that\n"
127 " o - Only listen to audio coming from this channel.\n"
128 " X - Allow the user to exit ChanSpy to a valid single digit\n"
129 " numeric extension in the current context or the context\n"
130 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
131 " name of the last channel that was spied on will be stored\n"
132 " in the SPY_CHANNEL variable.\n"
136 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
137 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
138 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
139 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
140 OPTION_RECORD = (1 << 4),
141 OPTION_WHISPER = (1 << 5),
142 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
143 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
144 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
145 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
156 AST_APP_OPTIONS(spy_opts, {
157 AST_APP_OPTION('q', OPTION_QUIET),
158 AST_APP_OPTION('b', OPTION_BRIDGED),
159 AST_APP_OPTION('w', OPTION_WHISPER),
160 AST_APP_OPTION('W', OPTION_PRIVATE),
161 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
162 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
163 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
164 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
165 AST_APP_OPTION('o', OPTION_READONLY),
166 AST_APP_OPTION('X', OPTION_EXIT),
170 struct chanspy_translation_helper {
172 struct ast_audiohook spy_audiohook;
173 struct ast_audiohook whisper_audiohook;
178 static void *spy_alloc(struct ast_channel *chan, void *data)
180 /* just store the data pointer in the channel structure */
184 static void spy_release(struct ast_channel *chan, void *data)
189 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
191 struct chanspy_translation_helper *csth = data;
192 struct ast_frame *f = NULL;
194 ast_audiohook_lock(&csth->spy_audiohook);
195 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
196 /* Channel is already gone more than likely */
197 ast_audiohook_unlock(&csth->spy_audiohook);
201 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
203 ast_audiohook_unlock(&csth->spy_audiohook);
208 if (ast_write(chan, f)) {
214 write(csth->fd, f->data, f->datalen);
221 static struct ast_generator spygen = {
223 .release = spy_release,
224 .generate = spy_generate,
227 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
230 struct ast_channel *peer = NULL;
232 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
234 res = ast_audiohook_attach(chan, audiohook);
236 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
237 ast_channel_unlock(chan);
238 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
240 ast_channel_unlock(chan);
246 struct ast_channel *chan;
250 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
251 int *volfactor, int fd, const struct ast_flags *flags, char *exitcontext)
253 struct chanspy_translation_helper csth;
254 int running = 0, res, x = 0;
258 struct ast_silence_generator *silgen = NULL;
259 struct ast_channel *spyee = NULL;
260 const char *spyer_name;
262 ast_channel_lock(chan);
263 spyer_name = ast_strdupa(chan->name);
264 ast_channel_unlock(chan);
266 ast_mutex_lock(&spyee_chanspy_ds->lock);
267 if (spyee_chanspy_ds->chan) {
268 spyee = spyee_chanspy_ds->chan;
269 ast_channel_lock(spyee);
271 ast_mutex_unlock(&spyee_chanspy_ds->lock);
276 /* We now hold the channel lock on spyee */
278 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
279 ast_channel_unlock(spyee);
283 name = ast_strdupa(spyee->name);
284 ast_verb(2, "Spying on channel %s\n", name);
286 memset(&csth, 0, sizeof(csth));
288 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
290 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { /* Unlocks spyee */
291 ast_audiohook_destroy(&csth.spy_audiohook);
295 if (ast_test_flag(flags, OPTION_WHISPER)) {
296 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
297 start_spying(spyee, spyer_name, &csth.whisper_audiohook); /* Unlocks spyee */
302 csth.volfactor = *volfactor;
304 if (csth.volfactor) {
305 csth.spy_audiohook.options.read_volume = csth.volfactor;
306 csth.spy_audiohook.options.write_volume = csth.volfactor;
311 if (ast_test_flag(flags, OPTION_PRIVATE))
312 silgen = ast_channel_start_silence_generator(chan);
314 ast_activate_generator(chan, &spygen, &csth);
316 /* We can no longer rely on 'spyee' being an actual channel;
317 it can be hung up and freed out from under us. However, the
318 channel destructor will put NULL into our csth.spy.chan
319 field when that happens, so that is our signal that the spyee
320 channel has gone away.
323 /* Note: it is very important that the ast_waitfor() be the first
324 condition in this expression, so that if we wait for some period
325 of time before receiving a frame from our spying channel, we check
326 for hangup on the spied-on channel _after_ knowing that a frame
327 has arrived, since the spied-on channel could have gone away while
330 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
331 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
336 if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
337 ast_audiohook_lock(&csth.whisper_audiohook);
338 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
339 ast_audiohook_unlock(&csth.whisper_audiohook);
344 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
349 if (x == sizeof(inp))
357 if (ast_test_flag(flags, OPTION_EXIT)) {
361 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
362 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
363 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
367 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
369 } else if (res >= '0' && res <= '9') {
376 } else if (res == '#') {
377 if (!ast_strlen_zero(inp)) {
385 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
387 csth.volfactor = *volfactor;
388 csth.spy_audiohook.options.read_volume = csth.volfactor;
389 csth.spy_audiohook.options.write_volume = csth.volfactor;
393 if (ast_test_flag(flags, OPTION_PRIVATE))
394 ast_channel_stop_silence_generator(chan, silgen);
396 ast_deactivate_generator(chan);
398 if (ast_test_flag(flags, OPTION_WHISPER)) {
399 ast_audiohook_lock(&csth.whisper_audiohook);
400 ast_audiohook_detach(&csth.whisper_audiohook);
401 ast_audiohook_unlock(&csth.whisper_audiohook);
402 ast_audiohook_destroy(&csth.whisper_audiohook);
405 ast_audiohook_lock(&csth.spy_audiohook);
406 ast_audiohook_detach(&csth.spy_audiohook);
407 ast_audiohook_unlock(&csth.spy_audiohook);
408 ast_audiohook_destroy(&csth.spy_audiohook);
410 ast_verb(2, "Done Spying on channel %s\n", name);
416 * \note This relies on the embedded lock to be recursive, as it may be called
417 * due to a call to chanspy_ds_free with the lock held there.
419 static void chanspy_ds_destroy(void *data)
421 struct chanspy_ds *chanspy_ds = data;
423 /* Setting chan to be NULL is an atomic operation, but we don't want this
424 * value to change while this lock is held. The lock is held elsewhere
425 * while it performs non-atomic operations with this channel pointer */
427 ast_mutex_lock(&chanspy_ds->lock);
428 chanspy_ds->chan = NULL;
429 ast_mutex_unlock(&chanspy_ds->lock);
432 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
434 struct chanspy_ds *chanspy_ds = data;
436 ast_mutex_lock(&chanspy_ds->lock);
437 chanspy_ds->chan = new_chan;
438 ast_mutex_unlock(&chanspy_ds->lock);
441 static const struct ast_datastore_info chanspy_ds_info = {
443 .destroy = chanspy_ds_destroy,
444 .chan_fixup = chanspy_ds_chan_fixup,
447 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
452 ast_mutex_lock(&chanspy_ds->lock);
453 if (chanspy_ds->chan) {
454 struct ast_datastore *datastore;
455 struct ast_channel *chan;
457 chan = chanspy_ds->chan;
459 ast_channel_lock(chan);
460 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, NULL))) {
461 ast_channel_datastore_remove(chan, datastore);
462 /* chanspy_ds->chan is NULL after this call */
463 chanspy_ds_destroy(datastore->data);
464 datastore->data = NULL;
465 ast_channel_datastore_free(datastore);
467 ast_channel_unlock(chan);
469 ast_mutex_unlock(&chanspy_ds->lock);
474 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
475 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
477 struct ast_datastore *datastore = NULL;
479 ast_mutex_lock(&chanspy_ds->lock);
481 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, NULL))) {
482 ast_mutex_unlock(&chanspy_ds->lock);
483 chanspy_ds = chanspy_ds_free(chanspy_ds);
484 ast_channel_unlock(chan);
488 chanspy_ds->chan = chan;
489 datastore->data = chanspy_ds;
490 ast_channel_datastore_add(chan, datastore);
495 static struct chanspy_ds *next_channel(struct ast_channel *chan,
496 const struct ast_channel *last, const char *spec,
497 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
499 struct ast_channel *next;
502 if (!ast_strlen_zero(spec))
503 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
504 else if (!ast_strlen_zero(exten))
505 next = ast_walk_channel_by_exten_locked(last, exten, context);
507 next = ast_channel_walk_locked(last);
512 if (!strncmp(next->name, "Zap/pseudo", 10)) {
513 ast_channel_unlock(next);
515 } else if (next == chan) {
517 ast_channel_unlock(next);
521 return setup_chanspy_ds(next, chanspy_ds);
524 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
525 int volfactor, const int fd, const char *mygroup, const char *myenforced,
526 const char *spec, const char *exten, const char *context)
528 char nameprefix[AST_NAME_STRLEN];
529 char peer_name[AST_NAME_STRLEN + 5];
530 char exitcontext[AST_MAX_CONTEXT] = "";
531 signed char zero_volume = 0;
536 int num_spyed_upon = 1;
537 struct chanspy_ds chanspy_ds;
539 if (ast_test_flag(flags, OPTION_EXIT)) {
541 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
542 ast_copy_string(exitcontext, c, sizeof(exitcontext));
543 else if (!ast_strlen_zero(chan->macrocontext))
544 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
546 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
549 ast_mutex_init(&chanspy_ds.lock);
551 if (chan->_state != AST_STATE_UP)
554 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
559 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
560 struct ast_channel *prev = NULL, *peer = NULL;
562 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
563 res = ast_streamfile(chan, "beep", chan->language);
565 res = ast_waitstream(chan, "");
567 ast_clear_flag(chan, AST_FLAG_SPYING);
570 if (!ast_strlen_zero(exitcontext)) {
574 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
577 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
581 res = ast_waitfordigit(chan, waitms);
583 ast_clear_flag(chan, AST_FLAG_SPYING);
586 if (!ast_strlen_zero(exitcontext)) {
590 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
593 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
596 /* reset for the next loop around, unless overridden later */
600 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
602 chanspy_ds_free(peer_chanspy_ds), prev = peer,
603 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
604 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
616 int ienf = !myenforced;
617 struct ast_channel *peer;
619 peer = peer_chanspy_ds->chan;
621 ast_mutex_unlock(&peer_chanspy_ds->lock);
624 ast_channel_unlock(peer);
625 chanspy_ds_free(peer_chanspy_ds);
629 if (ast_check_hangup(chan)) {
630 ast_channel_unlock(peer);
631 chanspy_ds_free(peer_chanspy_ds);
635 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
636 ast_channel_unlock(peer);
640 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
641 ast_channel_unlock(peer);
646 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
647 dup_group = ast_strdupa(group);
648 num_groups = ast_app_separate_args(dup_group, ':', groups,
649 sizeof(groups) / sizeof(groups[0]));
652 for (x = 0; x < num_groups; x++) {
653 if (!strcmp(mygroup, groups[x])) {
661 ast_channel_unlock(peer);
667 /* We don't need to allocate more space than just the
668 length of (peer->name) for ext as we will cut the
669 channel name's ending before copying into ext */
671 ext = alloca(strlen(peer->name));
673 form_enforced = alloca(strlen(myenforced) + 3);
675 strcpy(form_enforced, ":");
676 strcat(form_enforced, myenforced);
677 strcat(form_enforced, ":");
679 buffer = ast_strdupa(peer->name);
681 if ((end = strchr(buffer, '-'))) {
689 if (strcasestr(form_enforced, ext))
696 strcpy(peer_name, "spy-");
697 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
698 ptr = strchr(peer_name, '/');
701 for (s = peer_name; s < ptr; s++)
704 /* We have to unlock the peer channel here to avoid a deadlock.
705 * So, when we need it again, we have to lock the datastore and get
706 * the pointer from there to see if the channel is still valid. */
707 ast_channel_unlock(peer);
710 if (!ast_test_flag(flags, OPTION_QUIET)) {
711 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
712 res = ast_streamfile(chan, peer_name, chan->language);
714 res = ast_waitstream(chan, "");
716 chanspy_ds_free(peer_chanspy_ds);
720 res = ast_say_character_str(chan, peer_name, "", chan->language);
721 if ((num = atoi(ptr)))
722 ast_say_digits(chan, atoi(ptr), "", chan->language);
726 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
730 chanspy_ds_free(peer_chanspy_ds);
732 } else if (res == -2) {
734 chanspy_ds_free(peer_chanspy_ds);
736 } else if (res > 1 && spec) {
737 struct ast_channel *next;
739 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
741 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
742 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
743 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
745 /* stay on this channel, if it is still valid */
747 ast_mutex_lock(&peer_chanspy_ds->lock);
748 if (peer_chanspy_ds->chan) {
749 ast_channel_lock(peer_chanspy_ds->chan);
750 next_chanspy_ds = peer_chanspy_ds;
751 peer_chanspy_ds = NULL;
753 /* the channel is gone */
754 ast_mutex_unlock(&peer_chanspy_ds->lock);
755 next_chanspy_ds = NULL;
762 if (res == -1 || ast_check_hangup(chan))
767 ast_clear_flag(chan, AST_FLAG_SPYING);
769 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
771 ast_mutex_destroy(&chanspy_ds.lock);
776 static int chanspy_exec(struct ast_channel *chan, void *data)
778 char *myenforced = NULL;
779 char *mygroup = NULL;
780 char *recbase = NULL;
782 struct ast_flags flags;
786 AST_DECLARE_APP_ARGS(args,
788 AST_APP_ARG(options);
790 char *opts[OPT_ARG_ARRAY_SIZE];
792 data = ast_strdupa(data);
793 AST_STANDARD_APP_ARGS(args, data);
795 if (args.spec && !strcmp(args.spec, "all"))
799 ast_app_parse_options(spy_opts, &flags, opts, args.options);
800 if (ast_test_flag(&flags, OPTION_GROUP))
801 mygroup = opts[OPT_ARG_GROUP];
803 if (ast_test_flag(&flags, OPTION_RECORD) &&
804 !(recbase = opts[OPT_ARG_RECORD]))
807 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
810 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
811 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
816 if (ast_test_flag(&flags, OPTION_PRIVATE))
817 ast_set_flag(&flags, OPTION_WHISPER);
819 if (ast_test_flag(&flags, OPTION_ENFORCED))
820 myenforced = opts[OPT_ARG_ENFORCED];
823 ast_clear_flag(&flags, AST_FLAGS_ALL);
825 oldwf = chan->writeformat;
826 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
827 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
834 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
835 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
836 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
841 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL);
846 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
847 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
852 static int extenspy_exec(struct ast_channel *chan, void *data)
854 char *ptr, *exten = NULL;
855 char *mygroup = NULL;
856 char *recbase = NULL;
858 struct ast_flags flags;
862 AST_DECLARE_APP_ARGS(args,
863 AST_APP_ARG(context);
864 AST_APP_ARG(options);
867 data = ast_strdupa(data);
869 AST_STANDARD_APP_ARGS(args, data);
870 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
871 exten = args.context;
876 if (ast_strlen_zero(args.context))
877 args.context = ast_strdupa(chan->context);
880 char *opts[OPT_ARG_ARRAY_SIZE];
882 ast_app_parse_options(spy_opts, &flags, opts, args.options);
883 if (ast_test_flag(&flags, OPTION_GROUP))
884 mygroup = opts[OPT_ARG_GROUP];
886 if (ast_test_flag(&flags, OPTION_RECORD) &&
887 !(recbase = opts[OPT_ARG_RECORD]))
890 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
893 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
894 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
899 if (ast_test_flag(&flags, OPTION_PRIVATE))
900 ast_set_flag(&flags, OPTION_WHISPER);
902 ast_clear_flag(&flags, AST_FLAGS_ALL);
904 oldwf = chan->writeformat;
905 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
906 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
913 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
914 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
915 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
921 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context);
926 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
927 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
932 static int unload_module(void)
936 res |= ast_unregister_application(app_chan);
937 res |= ast_unregister_application(app_ext);
942 static int load_module(void)
946 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
947 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
952 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");