re-doxygen some comments
[asterisk-bristuff.git] / apps / app_chanspy.c
blobb848351966228e8726e17f2e47cd0c9d38986623
1 /*
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
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>
27 * \ingroup applications
30 #include "asterisk.h"
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <ctype.h>
40 #include "asterisk/file.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/chanspy.h"
44 #include "asterisk/features.h"
45 #include "asterisk/options.h"
46 #include "asterisk/app.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/say.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/translate.h"
51 #include "asterisk/module.h"
52 #include "asterisk/lock.h"
54 #define AST_NAME_STRLEN 256
56 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
57 static const char *app_chan = "ChanSpy";
58 static const char *desc_chan =
59 " ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
60 "audio from an Asterisk channel. This includes the audio coming in and\n"
61 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
62 "only channels beginning with this string will be spied upon.\n"
63 " While spying, the following actions may be performed:\n"
64 " - Dialing # cycles the volume level.\n"
65 " - Dialing * will stop spying and look for another channel to spy on.\n"
66 " - Dialing a series of digits followed by # builds a channel name to append\n"
67 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
68 " the digits '1234#' while spying will begin spying on the channel\n"
69 " 'Agent/1234'.\n"
70 " Options:\n"
71 " b - Only spy on channels involved in a bridged call.\n"
72 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
73 " contain 'grp' in an optional : delimited list.\n"
74 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
75 " selected channel name.\n"
76 " r[(basename)] - Record the session to the monitor spool directory. An\n"
77 " optional base for the filename may be specified. The\n"
78 " default is 'chanspy'.\n"
79 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
80 " negative value refers to a quieter setting.\n"
81 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
82 " the spied-on channel.\n"
83 " W - Enable 'private whisper' mode, so the spying channel can\n"
84 " talk to the spied-on channel but cannot listen to that\n"
85 " channel.\n"
88 static const char *app_ext = "ExtenSpy";
89 static const char *desc_ext =
90 " ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
91 "audio from an Asterisk channel. This includes the audio coming in and\n"
92 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
93 "specified extension will be selected for spying. If the optional context is not\n"
94 "supplied, the current channel's context will be used.\n"
95 " While spying, the following actions may be performed:\n"
96 " - Dialing # cycles the volume level.\n"
97 " - Dialing * will stop spying and look for another channel to spy on.\n"
98 " Options:\n"
99 " b - Only spy on channels involved in a bridged call.\n"
100 " g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
101 " contain 'grp' in an optional : delimited list.\n"
102 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
103 " selected channel name.\n"
104 " r[(basename)] - Record the session to the monitor spool directory. An\n"
105 " optional base for the filename may be specified. The\n"
106 " default is 'chanspy'.\n"
107 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
108 " negative value refers to a quieter setting.\n"
109 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
110 " the spied-on channel.\n"
111 " W - Enable 'private whisper' mode, so the spying channel can\n"
112 " talk to the spied-on channel but cannot listen to that\n"
113 " channel.\n"
116 enum {
117 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
118 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
119 OPTION_VOLUME = (1 << 2), /* Specify initial volume */
120 OPTION_GROUP = (1 << 3), /* Only look at channels in group */
121 OPTION_RECORD = (1 << 4),
122 OPTION_WHISPER = (1 << 5),
123 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
124 } chanspy_opt_flags;
126 enum {
127 OPT_ARG_VOLUME = 0,
128 OPT_ARG_GROUP,
129 OPT_ARG_RECORD,
130 OPT_ARG_ARRAY_SIZE,
131 } chanspy_opt_args;
133 AST_APP_OPTIONS(spy_opts, {
134 AST_APP_OPTION('q', OPTION_QUIET),
135 AST_APP_OPTION('b', OPTION_BRIDGED),
136 AST_APP_OPTION('w', OPTION_WHISPER),
137 AST_APP_OPTION('W', OPTION_PRIVATE),
138 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
139 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
140 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
144 struct chanspy_translation_helper {
145 /* spy data */
146 struct ast_channel_spy spy;
147 int fd;
148 int volfactor;
151 static void *spy_alloc(struct ast_channel *chan, void *data)
153 /* just store the data pointer in the channel structure */
154 return data;
157 static void spy_release(struct ast_channel *chan, void *data)
159 /* nothing to do */
162 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
164 struct chanspy_translation_helper *csth = data;
165 struct ast_frame *f;
167 if (csth->spy.status != CHANSPY_RUNNING)
168 /* Channel is already gone more than likely */
169 return -1;
171 ast_mutex_lock(&csth->spy.lock);
172 f = ast_channel_spy_read_frame(&csth->spy, samples);
173 ast_mutex_unlock(&csth->spy.lock);
175 if (!f)
176 return 0;
178 if (ast_write(chan, f)) {
179 ast_frfree(f);
180 return -1;
183 if (csth->fd)
184 write(csth->fd, f->data, f->datalen);
186 ast_frfree(f);
188 return 0;
191 static struct ast_generator spygen = {
192 .alloc = spy_alloc,
193 .release = spy_release,
194 .generate = spy_generate,
197 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy)
199 int res;
200 struct ast_channel *peer;
202 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
204 ast_channel_lock(chan);
205 res = ast_channel_spy_add(chan, spy);
206 ast_channel_unlock(chan);
208 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
209 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
211 return res;
214 /* Map 'volume' levels from -4 through +4 into
215 decibel (dB) settings for channel drivers
217 static signed char volfactor_map[] = {
218 -24,
219 -18,
220 -12,
229 /* attempt to set the desired gain adjustment via the channel driver;
230 if successful, clear it out of the csth structure so the
231 generator will not attempt to do the adjustment itself
233 static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
235 signed char volume_adjust = volfactor_map[csth->volfactor + 4];
237 if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0))
238 csth->volfactor = 0;
239 csth->spy.read_vol_adjustment = csth->volfactor;
240 csth->spy.write_vol_adjustment = csth->volfactor;
243 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
244 const struct ast_flags *flags)
246 struct chanspy_translation_helper csth;
247 int running = 0, res, x = 0;
248 char inp[24] = {0};
249 char *name;
250 struct ast_frame *f;
251 struct ast_silence_generator *silgen = NULL;
253 if (ast_check_hangup(chan) || ast_check_hangup(spyee))
254 return 0;
256 name = ast_strdupa(spyee->name);
257 if (option_verbose >= 2)
258 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
260 memset(&csth, 0, sizeof(csth));
261 ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
262 ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
263 ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
264 csth.spy.type = "ChanSpy";
265 csth.spy.status = CHANSPY_RUNNING;
266 csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
267 csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
268 ast_mutex_init(&csth.spy.lock);
269 csth.volfactor = *volfactor;
270 set_volume(chan, &csth);
271 if (csth.volfactor) {
272 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
273 csth.spy.read_vol_adjustment = csth.volfactor;
274 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
275 csth.spy.write_vol_adjustment = csth.volfactor;
277 csth.fd = fd;
279 if (start_spying(spyee, chan, &csth.spy)) {
280 ast_mutex_destroy(&csth.spy.lock);
281 return 0;
284 if (ast_test_flag(flags, OPTION_WHISPER)) {
285 struct ast_filestream *beepstream;
286 int old_write_format = 0;
288 ast_channel_whisper_start(csth.spy.chan);
289 old_write_format = chan->writeformat;
290 if ((beepstream = ast_openstream_full(chan, "beep", chan->language, 1))) {
291 struct ast_frame *f;
293 while ((f = ast_readframe(beepstream))) {
294 ast_channel_whisper_feed(csth.spy.chan, f);
295 ast_frfree(f);
298 ast_closestream(beepstream);
299 chan->stream = NULL;
301 if (old_write_format)
302 ast_set_write_format(chan, old_write_format);
305 if (ast_test_flag(flags, OPTION_PRIVATE))
306 silgen = ast_channel_start_silence_generator(chan);
307 else
308 ast_activate_generator(chan, &spygen, &csth);
310 /* We can no longer rely on 'spyee' being an actual channel;
311 it can be hung up and freed out from under us. However, the
312 channel destructor will put NULL into our csth.spy.chan
313 field when that happens, so that is our signal that the spyee
314 channel has gone away.
317 /* Note: it is very important that the ast_waitfor() be the first
318 condition in this expression, so that if we wait for some period
319 of time before receiving a frame from our spying channel, we check
320 for hangup on the spied-on channel _after_ knowing that a frame
321 has arrived, since the spied-on channel could have gone away while
322 we were waiting
324 while ((res = ast_waitfor(chan, -1) > -1) &&
325 csth.spy.status == CHANSPY_RUNNING &&
326 csth.spy.chan) {
327 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
328 running = -1;
329 break;
332 if (ast_test_flag(flags, OPTION_WHISPER) &&
333 (f->frametype == AST_FRAME_VOICE)) {
334 ast_channel_whisper_feed(csth.spy.chan, f);
335 ast_frfree(f);
336 continue;
339 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
340 ast_frfree(f);
341 if (!res)
342 continue;
344 if (x == sizeof(inp))
345 x = 0;
347 if (res < 0) {
348 running = -1;
349 break;
352 if (res == '*') {
353 running = 0;
354 break;
355 } else if (res == '#') {
356 if (!ast_strlen_zero(inp)) {
357 running = atoi(inp);
358 break;
361 (*volfactor)++;
362 if (*volfactor > 4)
363 *volfactor = -4;
364 if (option_verbose > 2)
365 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
366 csth.volfactor = *volfactor;
367 set_volume(chan, &csth);
368 if (csth.volfactor) {
369 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
370 csth.spy.read_vol_adjustment = csth.volfactor;
371 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
372 csth.spy.write_vol_adjustment = csth.volfactor;
373 } else {
374 ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
375 ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
377 } else if (res >= '0' && res <= '9') {
378 inp[x++] = res;
382 if (ast_test_flag(flags, OPTION_WHISPER) && csth.spy.chan)
383 ast_channel_whisper_stop(csth.spy.chan);
385 if (ast_test_flag(flags, OPTION_PRIVATE))
386 ast_channel_stop_silence_generator(chan, silgen);
387 else
388 ast_deactivate_generator(chan);
390 csth.spy.status = CHANSPY_DONE;
392 /* If a channel still exists on our spy structure then we need to remove ourselves */
393 if (csth.spy.chan) {
394 ast_channel_lock(csth.spy.chan);
395 ast_channel_spy_remove(csth.spy.chan, &csth.spy);
396 ast_channel_unlock(csth.spy.chan);
398 ast_channel_spy_free(&csth.spy);
400 if (option_verbose >= 2)
401 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
403 return running;
406 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
407 const char *exten, const char *context)
409 struct ast_channel *this;
411 redo:
412 if (spec)
413 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
414 else if (exten)
415 this = ast_walk_channel_by_exten_locked(last, exten, context);
416 else
417 this = ast_channel_walk_locked(last);
419 if (this) {
420 ast_channel_unlock(this);
421 if (!strncmp(this->name, "Zap/pseudo", 10))
422 goto redo;
425 return this;
428 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
429 int volfactor, const int fd, const char *mygroup, const char *spec,
430 const char *exten, const char *context)
432 struct ast_channel *peer, *prev, *next;
433 char nameprefix[AST_NAME_STRLEN];
434 char peer_name[AST_NAME_STRLEN + 5];
435 signed char zero_volume = 0;
436 int waitms;
437 int res;
438 char *ptr;
439 int num;
441 if (chan->_state != AST_STATE_UP)
442 ast_answer(chan);
444 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
446 waitms = 100;
448 for (;;) {
449 if (!ast_test_flag(flags, OPTION_QUIET)) {
450 res = ast_streamfile(chan, "beep", chan->language);
451 if (!res)
452 res = ast_waitstream(chan, "");
453 else if (res < 0) {
454 ast_clear_flag(chan, AST_FLAG_SPYING);
455 break;
459 res = ast_waitfordigit(chan, waitms);
460 if (res < 0) {
461 ast_clear_flag(chan, AST_FLAG_SPYING);
462 break;
465 /* reset for the next loop around, unless overridden later */
466 waitms = 100;
467 peer = prev = next = NULL;
469 for (peer = next_channel(peer, spec, exten, context);
470 peer;
471 prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
472 const char *group;
473 int igrp = !mygroup;
474 char *groups[25];
475 int num_groups = 0;
476 char *dup_group;
477 int x;
478 char *s;
480 if (peer == prev)
481 break;
483 if (peer == chan)
484 continue;
486 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
487 continue;
489 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
490 continue;
492 if (mygroup) {
493 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
494 dup_group = ast_strdupa(group);
495 num_groups = ast_app_separate_args(dup_group, ':', groups,
496 sizeof(groups) / sizeof(groups[0]));
499 for (x = 0; x < num_groups; x++) {
500 if (!strcmp(mygroup, groups[x])) {
501 igrp = 1;
502 break;
507 if (!igrp)
508 continue;
510 strcpy(peer_name, "spy-");
511 strncat(peer_name, peer->name, AST_NAME_STRLEN);
512 ptr = strchr(peer_name, '/');
513 *ptr++ = '\0';
515 for (s = peer_name; s < ptr; s++)
516 *s = tolower(*s);
518 if (!ast_test_flag(flags, OPTION_QUIET)) {
519 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
520 res = ast_streamfile(chan, peer_name, chan->language);
521 if (!res)
522 res = ast_waitstream(chan, "");
523 if (res)
524 break;
525 } else
526 res = ast_say_character_str(chan, peer_name, "", chan->language);
527 if ((num = atoi(ptr)))
528 ast_say_digits(chan, atoi(ptr), "", chan->language);
531 waitms = 5000;
532 res = channel_spy(chan, peer, &volfactor, fd, flags);
534 if (res == -1) {
535 break;
536 } else if (res > 1 && spec) {
537 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
538 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
539 ast_channel_unlock(next);
540 } else {
541 /* stay on this channel */
542 next = peer;
544 peer = NULL;
547 if (res == -1)
548 break;
551 ast_clear_flag(chan, AST_FLAG_SPYING);
553 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
555 return res;
558 static int chanspy_exec(struct ast_channel *chan, void *data)
560 struct ast_module_user *u;
561 char *options = NULL;
562 char *spec = NULL;
563 char *argv[2];
564 char *mygroup = NULL;
565 char *recbase = NULL;
566 int fd = 0;
567 struct ast_flags flags;
568 int oldwf = 0;
569 int argc = 0;
570 int volfactor = 0;
571 int res;
573 data = ast_strdupa(data);
575 u = ast_module_user_add(chan);
577 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
578 spec = argv[0];
579 if (argc > 1)
580 options = argv[1];
582 if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
583 spec = NULL;
586 if (options) {
587 char *opts[OPT_ARG_ARRAY_SIZE];
589 ast_app_parse_options(spy_opts, &flags, opts, options);
590 if (ast_test_flag(&flags, OPTION_GROUP))
591 mygroup = opts[OPT_ARG_GROUP];
593 if (ast_test_flag(&flags, OPTION_RECORD) &&
594 !(recbase = opts[OPT_ARG_RECORD]))
595 recbase = "chanspy";
597 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
598 int vol;
600 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
601 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
602 else
603 volfactor = vol;
606 if (ast_test_flag(&flags, OPTION_PRIVATE))
607 ast_set_flag(&flags, OPTION_WHISPER);
608 } else
609 ast_clear_flag(&flags, AST_FLAGS_ALL);
611 oldwf = chan->writeformat;
612 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
613 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
614 ast_module_user_remove(u);
615 return -1;
618 if (recbase) {
619 char filename[512];
621 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
622 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
623 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
624 fd = 0;
628 res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
630 if (fd)
631 close(fd);
633 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
634 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
636 ast_module_user_remove(u);
638 return res;
641 static int extenspy_exec(struct ast_channel *chan, void *data)
643 struct ast_module_user *u;
644 char *options = NULL;
645 char *exten = NULL;
646 char *context = NULL;
647 char *argv[2];
648 char *mygroup = NULL;
649 char *recbase = NULL;
650 int fd = 0;
651 struct ast_flags flags;
652 int oldwf = 0;
653 int argc = 0;
654 int volfactor = 0;
655 int res;
657 data = ast_strdupa(data);
659 u = ast_module_user_add(chan);
661 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
662 context = argv[0];
663 if (!ast_strlen_zero(argv[0]))
664 exten = strsep(&context, "@");
665 if (ast_strlen_zero(context))
666 context = ast_strdupa(chan->context);
667 if (argc > 1)
668 options = argv[1];
671 if (options) {
672 char *opts[OPT_ARG_ARRAY_SIZE];
674 ast_app_parse_options(spy_opts, &flags, opts, options);
675 if (ast_test_flag(&flags, OPTION_GROUP))
676 mygroup = opts[OPT_ARG_GROUP];
678 if (ast_test_flag(&flags, OPTION_RECORD) &&
679 !(recbase = opts[OPT_ARG_RECORD]))
680 recbase = "chanspy";
682 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
683 int vol;
685 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
686 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
687 else
688 volfactor = vol;
691 if (ast_test_flag(&flags, OPTION_PRIVATE))
692 ast_set_flag(&flags, OPTION_WHISPER);
693 } else
694 ast_clear_flag(&flags, AST_FLAGS_ALL);
696 oldwf = chan->writeformat;
697 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
698 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
699 ast_module_user_remove(u);
700 return -1;
703 if (recbase) {
704 char filename[512];
706 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
707 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
708 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
709 fd = 0;
713 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
715 if (fd)
716 close(fd);
718 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
719 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
721 ast_module_user_remove(u);
723 return res;
726 static int unload_module(void)
728 int res = 0;
730 res |= ast_unregister_application(app_chan);
731 res |= ast_unregister_application(app_ext);
733 ast_module_user_hangup_all();
735 return res;
738 static int load_module(void)
740 int res = 0;
742 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
743 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
745 return res;
748 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");