2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5 * Copyright (C) 2005 - 2006, 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>
27 * \ingroup applications
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
37 #include "asterisk/file.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/audiohook.h"
40 #include "asterisk/features.h"
41 #include "asterisk/app.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/say.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/translate.h"
46 #include "asterisk/module.h"
47 #include "asterisk/lock.h"
49 #define AST_NAME_STRLEN 256
51 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
52 static const char *app_chan = "ChanSpy";
53 static const char *desc_chan =
54 " ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
55 "audio from an Asterisk channel. This includes the audio coming in and\n"
56 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
57 "only channels beginning with this string will be spied upon.\n"
58 " While spying, the following actions may be performed:\n"
59 " - Dialing # cycles the volume level.\n"
60 " - Dialing * will stop spying and look for another channel to spy on.\n"
61 " - Dialing a series of digits followed by # builds a channel name to append\n"
62 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
63 " the digits '1234#' while spying will begin spying on the channel\n"
65 " Note: The X option supersedes the three features above in that if a valid\n"
66 " single digit extension exists in the correct context ChanSpy will\n"
67 " exit to it. This also disables choosing a channel based on 'chanprefix'\n"
68 " and a digit sequence.\n"
70 " b - Only spy on channels involved in a bridged call.\n"
71 " g(grp) - Match only channels where their SPYGROUP variable is set to\n"
72 " contain 'grp' in an optional : delimited list.\n"
73 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
74 " selected channel name.\n"
75 " r[(basename)] - Record the session to the monitor spool directory. An\n"
76 " optional base for the filename may be specified. The\n"
77 " default is 'chanspy'.\n"
78 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
79 " negative value refers to a quieter setting.\n"
80 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
81 " the spied-on channel.\n"
82 " W - Enable 'private whisper' mode, so the spying channel can\n"
83 " talk to the spied-on channel but cannot listen to that\n"
85 " o - Only listen to audio coming from this channel.\n"
86 " X - Allow the user to exit ChanSpy to a valid single digit\n"
87 " numeric extension in the current context or the context\n"
88 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
89 " name of the last channel that was spied on will be stored\n"
90 " in the SPY_CHANNEL variable.\n"
91 " e(ext) - Enable 'enforced' mode, so the spying channel can\n"
92 " only monitor extensions whose name is in the 'ext' : \n"
96 static const char *app_ext = "ExtenSpy";
97 static const char *desc_ext =
98 " ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
99 "audio from an Asterisk channel. This includes the audio coming in and\n"
100 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
101 "specified extension will be selected for spying. If the optional context is not\n"
102 "supplied, the current channel's context will be used.\n"
103 " While spying, the following actions may be performed:\n"
104 " - Dialing # cycles the volume level.\n"
105 " - Dialing * will stop spying and look for another channel to spy on.\n"
106 " Note: The X option superseeds the two features above in that if a valid\n"
107 " single digit extension exists in the correct context it ChanSpy will\n"
110 " b - Only spy on channels involved in a bridged call.\n"
111 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
112 " contain 'grp' in an optional : delimited list.\n"
113 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
114 " selected channel name.\n"
115 " r[(basename)] - Record the session to the monitor spool directory. An\n"
116 " optional base for the filename may be specified. The\n"
117 " default is 'chanspy'.\n"
118 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
119 " negative value refers to a quieter setting.\n"
120 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
121 " the spied-on channel.\n"
122 " W - Enable 'private whisper' mode, so the spying channel can\n"
123 " talk to the spied-on channel but cannot listen to that\n"
125 " o - Only listen to audio coming from this channel.\n"
126 " X - Allow the user to exit ChanSpy to a valid single digit\n"
127 " numeric extension in the current context or the context\n"
128 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
129 " name of the last channel that was spied on will be stored\n"
130 " in the SPY_CHANNEL variable.\n"
134 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
135 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
136 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
137 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
138 OPTION_RECORD = (1 << 4),
139 OPTION_WHISPER = (1 << 5),
140 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
141 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
142 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
143 OPTION_ENFORCED = (1 << 9), /* Enforced mode */
154 AST_APP_OPTIONS(spy_opts, {
155 AST_APP_OPTION('q', OPTION_QUIET),
156 AST_APP_OPTION('b', OPTION_BRIDGED),
157 AST_APP_OPTION('w', OPTION_WHISPER),
158 AST_APP_OPTION('W', OPTION_PRIVATE),
159 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
160 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
161 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
162 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
163 AST_APP_OPTION('o', OPTION_READONLY),
164 AST_APP_OPTION('X', OPTION_EXIT),
168 struct chanspy_translation_helper {
170 struct ast_audiohook spy_audiohook;
171 struct ast_audiohook whisper_audiohook;
176 static void *spy_alloc(struct ast_channel *chan, void *data)
178 /* just store the data pointer in the channel structure */
182 static void spy_release(struct ast_channel *chan, void *data)
187 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
189 struct chanspy_translation_helper *csth = data;
190 struct ast_frame *f = NULL;
192 ast_audiohook_lock(&csth->spy_audiohook);
193 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
194 /* Channel is already gone more than likely */
195 ast_audiohook_unlock(&csth->spy_audiohook);
199 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
201 ast_audiohook_unlock(&csth->spy_audiohook);
206 if (ast_write(chan, f)) {
212 write(csth->fd, f->data, f->datalen);
219 static struct ast_generator spygen = {
221 .release = spy_release,
222 .generate = spy_generate,
225 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
228 struct ast_channel *peer = NULL;
230 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
232 res = ast_audiohook_attach(chan, audiohook);
234 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
235 ast_channel_unlock(chan);
236 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
238 ast_channel_unlock(chan);
244 struct ast_channel *chan;
248 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
249 int *volfactor, int fd, const struct ast_flags *flags, char *exitcontext)
251 struct chanspy_translation_helper csth;
252 int running = 0, res, x = 0;
256 struct ast_silence_generator *silgen = NULL;
257 struct ast_channel *spyee = NULL;
258 const char *spyer_name;
260 ast_channel_lock(chan);
261 spyer_name = ast_strdupa(chan->name);
262 ast_channel_unlock(chan);
264 ast_mutex_lock(&spyee_chanspy_ds->lock);
265 if (spyee_chanspy_ds->chan) {
266 spyee = spyee_chanspy_ds->chan;
267 ast_channel_lock(spyee);
269 ast_mutex_unlock(&spyee_chanspy_ds->lock);
274 /* We now hold the channel lock on spyee */
276 if (ast_check_hangup(chan) || ast_check_hangup(spyee))
279 name = ast_strdupa(spyee->name);
280 ast_verb(2, "Spying on channel %s\n", name);
282 memset(&csth, 0, sizeof(csth));
284 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
286 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { /* Unlocks spyee */
287 ast_audiohook_destroy(&csth.spy_audiohook);
291 if (ast_test_flag(flags, OPTION_WHISPER)) {
292 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
293 start_spying(spyee, spyer_name, &csth.whisper_audiohook); /* Unlocks spyee */
298 csth.volfactor = *volfactor;
300 if (csth.volfactor) {
301 csth.spy_audiohook.options.read_volume = csth.volfactor;
302 csth.spy_audiohook.options.write_volume = csth.volfactor;
307 if (ast_test_flag(flags, OPTION_PRIVATE))
308 silgen = ast_channel_start_silence_generator(chan);
310 ast_activate_generator(chan, &spygen, &csth);
312 /* We can no longer rely on 'spyee' being an actual channel;
313 it can be hung up and freed out from under us. However, the
314 channel destructor will put NULL into our csth.spy.chan
315 field when that happens, so that is our signal that the spyee
316 channel has gone away.
319 /* Note: it is very important that the ast_waitfor() be the first
320 condition in this expression, so that if we wait for some period
321 of time before receiving a frame from our spying channel, we check
322 for hangup on the spied-on channel _after_ knowing that a frame
323 has arrived, since the spied-on channel could have gone away while
326 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
327 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
332 if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
333 ast_audiohook_lock(&csth.whisper_audiohook);
334 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
335 ast_audiohook_unlock(&csth.whisper_audiohook);
340 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
345 if (x == sizeof(inp))
353 if (ast_test_flag(flags, OPTION_EXIT)) {
357 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
358 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
359 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
363 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
365 } else if (res >= '0' && res <= '9') {
372 } else if (res == '#') {
373 if (!ast_strlen_zero(inp)) {
381 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
383 csth.volfactor = *volfactor;
384 csth.spy_audiohook.options.read_volume = csth.volfactor;
385 csth.spy_audiohook.options.write_volume = csth.volfactor;
389 if (ast_test_flag(flags, OPTION_PRIVATE))
390 ast_channel_stop_silence_generator(chan, silgen);
392 ast_deactivate_generator(chan);
394 if (ast_test_flag(flags, OPTION_WHISPER)) {
395 ast_audiohook_lock(&csth.whisper_audiohook);
396 ast_audiohook_detach(&csth.whisper_audiohook);
397 ast_audiohook_unlock(&csth.whisper_audiohook);
398 ast_audiohook_destroy(&csth.whisper_audiohook);
401 ast_audiohook_lock(&csth.spy_audiohook);
402 ast_audiohook_detach(&csth.spy_audiohook);
403 ast_audiohook_unlock(&csth.spy_audiohook);
404 ast_audiohook_destroy(&csth.spy_audiohook);
406 ast_verb(2, "Done Spying on channel %s\n", name);
412 * \note This relies on the embedded lock to be recursive, as it may be called
413 * due to a call to chanspy_ds_free with the lock held there.
415 static void chanspy_ds_destroy(void *data)
417 struct chanspy_ds *chanspy_ds = data;
419 /* Setting chan to be NULL is an atomic operation, but we don't want this
420 * value to change while this lock is held. The lock is held elsewhere
421 * while it performs non-atomic operations with this channel pointer */
423 ast_mutex_lock(&chanspy_ds->lock);
424 chanspy_ds->chan = NULL;
425 ast_mutex_unlock(&chanspy_ds->lock);
428 static const struct ast_datastore_info chanspy_ds_info = {
430 .destroy = chanspy_ds_destroy,
433 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
438 ast_mutex_lock(&chanspy_ds->lock);
439 if (chanspy_ds->chan) {
440 struct ast_datastore *datastore;
441 struct ast_channel *chan;
443 chan = chanspy_ds->chan;
445 ast_channel_lock(chan);
446 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, NULL))) {
447 ast_channel_datastore_remove(chan, datastore);
448 /* chanspy_ds->chan is NULL after this call */
449 ast_channel_datastore_free(datastore);
451 ast_channel_unlock(chan);
453 ast_mutex_unlock(&chanspy_ds->lock);
458 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
459 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
461 struct ast_datastore *datastore = NULL;
463 ast_mutex_lock(&chanspy_ds->lock);
465 chanspy_ds->chan = chan;
467 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, NULL))) {
468 chanspy_ds = chanspy_ds_free(chanspy_ds);
469 ast_channel_unlock(chan);
473 datastore->data = chanspy_ds;
475 ast_channel_datastore_add(chan, datastore);
480 static struct chanspy_ds *next_channel(struct ast_channel *chan,
481 const struct ast_channel *last, const char *spec,
482 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
484 struct ast_channel *this;
487 if (!ast_strlen_zero(spec))
488 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
490 else if (!ast_strlen_zero(exten))
491 this = ast_walk_channel_by_exten_locked(last, exten, context);
493 this = ast_channel_walk_locked(last);
498 if (!strncmp(this->name, "Zap/pseudo", 10)) {
499 ast_channel_unlock(this);
503 return setup_chanspy_ds(this, chanspy_ds);
506 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
507 int volfactor, const int fd, const char *mygroup, const char *myenforced,
508 const char *spec, const char *exten, const char *context)
510 char nameprefix[AST_NAME_STRLEN];
511 char peer_name[AST_NAME_STRLEN + 5];
512 char exitcontext[AST_MAX_CONTEXT] = "";
513 signed char zero_volume = 0;
518 int num_spyed_upon = 1;
519 struct chanspy_ds chanspy_ds;
521 if (ast_test_flag(flags, OPTION_EXIT)) {
523 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
524 ast_copy_string(exitcontext, c, sizeof(exitcontext));
525 else if (!ast_strlen_zero(chan->macrocontext))
526 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
528 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
531 ast_mutex_init(&chanspy_ds.lock);
533 if (chan->_state != AST_STATE_UP)
536 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
541 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
542 struct ast_channel *prev = NULL, *peer = NULL;
544 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
545 res = ast_streamfile(chan, "beep", chan->language);
547 res = ast_waitstream(chan, "");
549 ast_clear_flag(chan, AST_FLAG_SPYING);
552 if (!ast_strlen_zero(exitcontext)) {
556 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
559 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
563 res = ast_waitfordigit(chan, waitms);
565 ast_clear_flag(chan, AST_FLAG_SPYING);
568 if (!ast_strlen_zero(exitcontext)) {
572 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
575 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
578 /* reset for the next loop around, unless overridden later */
582 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
584 chanspy_ds_free(peer_chanspy_ds), prev = peer,
585 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
586 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
598 int ienf = !myenforced;
599 struct ast_channel *peer;
601 peer = peer_chanspy_ds->chan;
603 ast_mutex_unlock(&peer_chanspy_ds->lock);
606 ast_channel_unlock(peer);
607 chanspy_ds_free(peer_chanspy_ds);
612 ast_channel_unlock(peer);
616 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
617 ast_channel_unlock(peer);
621 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
622 ast_channel_unlock(peer);
627 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
628 dup_group = ast_strdupa(group);
629 num_groups = ast_app_separate_args(dup_group, ':', groups,
630 sizeof(groups) / sizeof(groups[0]));
633 for (x = 0; x < num_groups; x++) {
634 if (!strcmp(mygroup, groups[x])) {
642 ast_channel_unlock(peer);
648 /* We don't need to allocate more space than just the
649 length of (peer->name) for ext as we will cut the
650 channel name's ending before copying into ext */
652 ext = alloca(strlen(peer->name));
654 form_enforced = alloca(strlen(myenforced) + 3);
656 strcpy(form_enforced, ":");
657 strcat(form_enforced, myenforced);
658 strcat(form_enforced, ":");
660 buffer = ast_strdupa(peer->name);
662 if ((end = strchr(buffer, '-'))) {
670 if (strcasestr(form_enforced, ext))
677 strcpy(peer_name, "spy-");
678 strncat(peer_name, peer->name, AST_NAME_STRLEN);
679 ptr = strchr(peer_name, '/');
682 for (s = peer_name; s < ptr; s++)
685 /* We have to unlock the peer channel here to avoid a deadlock.
686 * So, when we need it again, we have to lock the datastore and get
687 * the pointer from there to see if the channel is still valid. */
688 ast_channel_unlock(peer);
691 if (!ast_test_flag(flags, OPTION_QUIET)) {
692 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
693 res = ast_streamfile(chan, peer_name, chan->language);
695 res = ast_waitstream(chan, "");
699 res = ast_say_character_str(chan, peer_name, "", chan->language);
700 if ((num = atoi(ptr)))
701 ast_say_digits(chan, atoi(ptr), "", chan->language);
705 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
710 } else if (res == -2) {
713 } else if (res > 1 && spec) {
714 struct ast_channel *next;
716 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
718 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
719 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
720 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
722 /* stay on this channel, if it is still valid */
724 ast_mutex_lock(&peer_chanspy_ds->lock);
725 if (peer_chanspy_ds->chan) {
726 ast_channel_lock(peer_chanspy_ds->chan);
727 next_chanspy_ds = peer_chanspy_ds;
728 peer_chanspy_ds = NULL;
730 /* the channel is gone */
731 ast_mutex_unlock(&peer_chanspy_ds->lock);
732 next_chanspy_ds = NULL;
742 ast_clear_flag(chan, AST_FLAG_SPYING);
744 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
746 ast_mutex_destroy(&chanspy_ds.lock);
751 static int chanspy_exec(struct ast_channel *chan, void *data)
753 char *myenforced = NULL;
754 char *mygroup = NULL;
755 char *recbase = NULL;
757 struct ast_flags flags;
761 AST_DECLARE_APP_ARGS(args,
763 AST_APP_ARG(options);
765 char *opts[OPT_ARG_ARRAY_SIZE];
767 data = ast_strdupa(data);
768 AST_STANDARD_APP_ARGS(args, data);
770 if (args.spec && !strcmp(args.spec, "all"))
774 ast_app_parse_options(spy_opts, &flags, opts, args.options);
775 if (ast_test_flag(&flags, OPTION_GROUP))
776 mygroup = opts[OPT_ARG_GROUP];
778 if (ast_test_flag(&flags, OPTION_RECORD) &&
779 !(recbase = opts[OPT_ARG_RECORD]))
782 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
785 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
786 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
791 if (ast_test_flag(&flags, OPTION_PRIVATE))
792 ast_set_flag(&flags, OPTION_WHISPER);
794 if (ast_test_flag(&flags, OPTION_ENFORCED))
795 myenforced = opts[OPT_ARG_ENFORCED];
798 ast_clear_flag(&flags, AST_FLAGS_ALL);
800 oldwf = chan->writeformat;
801 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
802 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
809 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
810 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
811 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
816 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL);
821 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
822 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
827 static int extenspy_exec(struct ast_channel *chan, void *data)
829 char *ptr, *exten = NULL;
830 char *mygroup = NULL;
831 char *recbase = NULL;
833 struct ast_flags flags;
837 AST_DECLARE_APP_ARGS(args,
838 AST_APP_ARG(context);
839 AST_APP_ARG(options);
842 data = ast_strdupa(data);
844 AST_STANDARD_APP_ARGS(args, data);
845 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
846 exten = args.context;
851 if (ast_strlen_zero(args.context))
852 args.context = ast_strdupa(chan->context);
855 char *opts[OPT_ARG_ARRAY_SIZE];
857 ast_app_parse_options(spy_opts, &flags, opts, args.options);
858 if (ast_test_flag(&flags, OPTION_GROUP))
859 mygroup = opts[OPT_ARG_GROUP];
861 if (ast_test_flag(&flags, OPTION_RECORD) &&
862 !(recbase = opts[OPT_ARG_RECORD]))
865 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
868 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
869 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
874 if (ast_test_flag(&flags, OPTION_PRIVATE))
875 ast_set_flag(&flags, OPTION_WHISPER);
877 ast_clear_flag(&flags, AST_FLAGS_ALL);
879 oldwf = chan->writeformat;
880 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
881 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
888 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
889 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
890 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
896 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context);
901 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
902 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
907 static int unload_module(void)
911 res |= ast_unregister_application(app_chan);
912 res |= ast_unregister_application(app_ext);
917 static int load_module(void)
921 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
922 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
927 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");