Merged revisions 43695 via svnmerge from
[asterisk/asterisk.git] / apps / app_chanspy.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
5  * Copyright (C) 2005 - 2006, Digium, Inc.
6  *
7  * A license has been granted to Digium (via disclaimer) for the use of
8  * this code.
9  *
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.
15  *
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.
19  */
20
21 /*! \file
22  *
23  * \brief ChanSpy: Listen in on any channel.
24  *
25  * \author Anthony Minessale II <anthmct@yahoo.com>
26  *
27  * \ingroup applications
28  */
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <ctype.h>
39
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"
53
54 #define AST_NAME_STRLEN 256
55
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"
86 ;
87
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"
114 ;
115
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;
125
126 enum {
127         OPT_ARG_VOLUME = 0,
128         OPT_ARG_GROUP,
129         OPT_ARG_RECORD,
130         OPT_ARG_ARRAY_SIZE,
131 } chanspy_opt_args;
132
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),
141 });
142
143
144 struct chanspy_translation_helper {
145         /* spy data */
146         struct ast_channel_spy spy;
147         int fd;
148         int volfactor;
149 };
150
151 static void *spy_alloc(struct ast_channel *chan, void *data)
152 {
153         /* just store the data pointer in the channel structure */
154         return data;
155 }
156
157 static void spy_release(struct ast_channel *chan, void *data)
158 {
159         /* nothing to do */
160 }
161
162 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) 
163 {
164         struct chanspy_translation_helper *csth = data;
165         struct ast_frame *f;
166                 
167         if (csth->spy.status != CHANSPY_RUNNING)
168                 /* Channel is already gone more than likely */
169                 return -1;
170
171         ast_mutex_lock(&csth->spy.lock);
172         f = ast_channel_spy_read_frame(&csth->spy, samples);
173         ast_mutex_unlock(&csth->spy.lock);
174                 
175         if (!f)
176                 return 0;
177                 
178         if (ast_write(chan, f)) {
179                 ast_frfree(f);
180                 return -1;
181         }
182
183         if (csth->fd)
184                 write(csth->fd, f->data, f->datalen);
185
186         ast_frfree(f);
187
188         return 0;
189 }
190
191 static struct ast_generator spygen = {
192         .alloc = spy_alloc,
193         .release = spy_release,
194         .generate = spy_generate, 
195 };
196
197 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy) 
198 {
199         int res;
200         struct ast_channel *peer;
201
202         ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
203
204         ast_channel_lock(chan);
205         res = ast_channel_spy_add(chan, spy);
206         ast_channel_unlock(chan);
207
208         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
209                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
210
211         return res;
212 }
213
214 /* Map 'volume' levels from -4 through +4 into
215    decibel (dB) settings for channel drivers
216 */
217 static signed char volfactor_map[] = {
218         -24,
219         -18,
220         -12,
221         -6,
222         0,
223         6,
224         12,
225         18,
226         24,
227 };
228
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
232 */
233 static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
234 {
235         signed char volume_adjust = volfactor_map[csth->volfactor + 4];
236
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;
241 }
242
243 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
244                        const struct ast_flags *flags) 
245 {
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;
252
253         if (ast_check_hangup(chan) || ast_check_hangup(spyee))
254                 return 0;
255
256         name = ast_strdupa(spyee->name);
257         if (option_verbose >= 2)
258                 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
259
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         csth.fd = fd;
272         
273         if (start_spying(spyee, chan, &csth.spy)) {
274                 ast_mutex_destroy(&csth.spy.lock);
275                 return 0;
276         }
277
278         if (ast_test_flag(flags, OPTION_WHISPER)) {
279                 struct ast_filestream *beepstream;
280                 int old_write_format = 0;
281
282                 ast_channel_whisper_start(csth.spy.chan);
283                 old_write_format = chan->writeformat;
284                 if ((beepstream = ast_openstream_full(chan, "beep", chan->language, 1))) {
285                         struct ast_frame *f;
286
287                         while ((f = ast_readframe(beepstream))) {
288                                 ast_channel_whisper_feed(csth.spy.chan, f);
289                                 ast_frfree(f);
290                         }
291
292                         ast_closestream(beepstream);
293                         chan->stream = NULL;
294                 }
295                 if (old_write_format)
296                         ast_set_write_format(chan, old_write_format);
297         }
298
299         if (ast_test_flag(flags, OPTION_PRIVATE))
300                 silgen = ast_channel_start_silence_generator(chan);
301         else
302                 ast_activate_generator(chan, &spygen, &csth);
303
304         /* We can no longer rely on 'spyee' being an actual channel;
305            it can be hung up and freed out from under us. However, the
306            channel destructor will put NULL into our csth.spy.chan
307            field when that happens, so that is our signal that the spyee
308            channel has gone away.
309         */
310
311         /* Note: it is very important that the ast_waitfor() be the first
312            condition in this expression, so that if we wait for some period
313            of time before receiving a frame from our spying channel, we check
314            for hangup on the spied-on channel _after_ knowing that a frame
315            has arrived, since the spied-on channel could have gone away while
316            we were waiting
317         */
318         while ((res = ast_waitfor(chan, -1) > -1) &&
319                csth.spy.status == CHANSPY_RUNNING &&
320                csth.spy.chan) {
321                 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
322                         running = -1;
323                         break;
324                 }
325
326                 if (ast_test_flag(flags, OPTION_WHISPER) &&
327                     (f->frametype == AST_FRAME_VOICE)) {
328                         ast_channel_whisper_feed(csth.spy.chan, f);
329                         ast_frfree(f);
330                         continue;
331                 }
332
333                 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
334                 ast_frfree(f);
335                 if (!res)
336                         continue;
337
338                 if (x == sizeof(inp))
339                         x = 0;
340
341                 if (res < 0) {
342                         running = -1;
343                         break;
344                 }
345
346                 if (res == '*') {
347                         running = 0;
348                         break;
349                 } else if (res == '#') {
350                         if (!ast_strlen_zero(inp)) {
351                                 running = atoi(inp);
352                                 break;
353                         }
354
355                         (*volfactor)++;
356                         if (*volfactor > 4)
357                                 *volfactor = -4;
358                         if (option_verbose > 2)
359                                 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
360                         csth.volfactor = *volfactor;
361                         set_volume(chan, &csth);
362                 } else if (res >= '0' && res <= '9') {
363                         inp[x++] = res;
364                 }
365         }
366
367         if (ast_test_flag(flags, OPTION_WHISPER) && csth.spy.chan)
368                 ast_channel_whisper_stop(csth.spy.chan);
369
370         if (ast_test_flag(flags, OPTION_PRIVATE))
371                 ast_channel_stop_silence_generator(chan, silgen);
372         else
373                 ast_deactivate_generator(chan);
374
375         /* If a channel still exists on our spy structure then we need to remove ourselves */
376         if (csth.spy.chan) {
377                 csth.spy.status = CHANSPY_DONE;
378                 ast_channel_lock(csth.spy.chan);
379                 ast_channel_spy_remove(csth.spy.chan, &csth.spy);
380                 ast_channel_unlock(csth.spy.chan);
381         }
382         ast_channel_spy_free(&csth.spy);
383         
384         if (option_verbose >= 2)
385                 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
386         
387         return running;
388 }
389
390 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
391                                         const char *exten, const char *context)
392 {
393         struct ast_channel *this;
394
395         redo:
396         if (spec)
397                 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
398         else if (exten)
399                 this = ast_walk_channel_by_exten_locked(last, exten, context);
400         else
401                 this = ast_channel_walk_locked(last);
402
403         if (this) {
404                 ast_channel_unlock(this);
405                 if (!strncmp(this->name, "Zap/pseudo", 10))
406                         goto redo;
407         }
408
409         return this;
410 }
411
412 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
413                        int volfactor, const int fd, const char *mygroup, const char *spec,
414                        const char *exten, const char *context)
415 {
416         struct ast_channel *peer, *prev, *next;
417         char nameprefix[AST_NAME_STRLEN];
418         char peer_name[AST_NAME_STRLEN + 5];
419         signed char zero_volume = 0;
420         int waitms;
421         int res;
422         char *ptr;
423         int num;
424
425         if (chan->_state != AST_STATE_UP)
426                 ast_answer(chan);
427
428         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
429
430         waitms = 100;
431
432         for (;;) {
433                 if (!ast_test_flag(flags, OPTION_QUIET)) {
434                         res = ast_streamfile(chan, "beep", chan->language);
435                         if (!res)
436                                 res = ast_waitstream(chan, "");
437                         else if (res < 0) {
438                                 ast_clear_flag(chan, AST_FLAG_SPYING);
439                                 break;
440                         }
441                 }
442
443                 res = ast_waitfordigit(chan, waitms);
444                 if (res < 0) {
445                         ast_clear_flag(chan, AST_FLAG_SPYING);
446                         break;
447                 }
448                                 
449                 /* reset for the next loop around, unless overridden later */
450                 waitms = 100;
451                 peer = prev = next = NULL;
452
453                 for (peer = next_channel(peer, spec, exten, context);
454                      peer;
455                      prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
456                         const char *group;
457                         int igrp = !mygroup;
458                         char *groups[25];
459                         int num_groups = 0;
460                         char *dup_group;
461                         int x;
462                         char *s;
463                                 
464                         if (peer == prev)
465                                 break;
466
467                         if (peer == chan)
468                                 continue;
469
470                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
471                                 continue;
472
473                         if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
474                                 continue;
475
476                         if (mygroup) {
477                                 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
478                                         dup_group = ast_strdupa(group);
479                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
480                                                                            sizeof(groups) / sizeof(groups[0]));
481                                 }
482                                 
483                                 for (x = 0; x < num_groups; x++) {
484                                         if (!strcmp(mygroup, groups[x])) {
485                                                 igrp = 1;
486                                                 break;
487                                         }
488                                 }
489                         }
490                         
491                         if (!igrp)
492                                 continue;
493
494                         strcpy(peer_name, "spy-");
495                         strncat(peer_name, peer->name, AST_NAME_STRLEN);
496                         ptr = strchr(peer_name, '/');
497                         *ptr++ = '\0';
498                         
499                         for (s = peer_name; s < ptr; s++)
500                                 *s = tolower(*s);
501                         
502                         if (!ast_test_flag(flags, OPTION_QUIET)) {
503                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
504                                         res = ast_streamfile(chan, peer_name, chan->language);
505                                         if (!res)
506                                                 res = ast_waitstream(chan, "");
507                                         if (res)
508                                                 break;
509                                 } else
510                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
511                                 if ((num = atoi(ptr))) 
512                                         ast_say_digits(chan, atoi(ptr), "", chan->language);
513                         }
514                         
515                         waitms = 5000;
516                         res = channel_spy(chan, peer, &volfactor, fd, flags);
517                         
518                         if (res == -1) {
519                                 break;
520                         } else if (res > 1 && spec) {
521                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
522                                 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
523                                         ast_channel_unlock(next);
524                                 } else {
525                                         /* stay on this channel */
526                                         next = peer;
527                                 }
528                                 peer = NULL;
529                         }
530                 }
531                 if (res == -1)
532                         break;
533         }
534         
535         ast_clear_flag(chan, AST_FLAG_SPYING);
536
537         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
538
539         return res;
540 }
541
542 static int chanspy_exec(struct ast_channel *chan, void *data)
543 {
544         struct ast_module_user *u;
545         char *options = NULL;
546         char *spec = NULL;
547         char *argv[2];
548         char *mygroup = NULL;
549         char *recbase = NULL;
550         int fd = 0;
551         struct ast_flags flags;
552         int oldwf = 0;
553         int argc = 0;
554         int volfactor = 0;
555         int res;
556
557         data = ast_strdupa(data);
558
559         u = ast_module_user_add(chan);
560
561         if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
562                 spec = argv[0];
563                 if (argc > 1)
564                         options = argv[1];
565
566                 if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
567                         spec = NULL;
568         }
569
570         if (options) {
571                 char *opts[OPT_ARG_ARRAY_SIZE];
572                 
573                 ast_app_parse_options(spy_opts, &flags, opts, options);
574                 if (ast_test_flag(&flags, OPTION_GROUP))
575                         mygroup = opts[OPT_ARG_GROUP];
576
577                 if (ast_test_flag(&flags, OPTION_RECORD) &&
578                     !(recbase = opts[OPT_ARG_RECORD]))
579                         recbase = "chanspy";
580
581                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
582                         int vol;
583
584                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
585                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
586                         else
587                                 volfactor = vol;
588                 }
589
590                 if (ast_test_flag(&flags, OPTION_PRIVATE))
591                         ast_set_flag(&flags, OPTION_WHISPER);
592         }
593
594         oldwf = chan->writeformat;
595         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
596                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
597                 ast_module_user_remove(u);
598                 return -1;
599         }
600
601         if (recbase) {
602                 char filename[512];
603
604                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
605                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
606                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
607                         fd = 0;
608                 }
609         }
610
611         res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
612
613         if (fd)
614                 close(fd);
615
616         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
617                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
618
619         ast_module_user_remove(u);
620
621         return res;
622 }
623
624 static int extenspy_exec(struct ast_channel *chan, void *data)
625 {
626         struct ast_module_user *u;
627         char *options = NULL;
628         char *exten = NULL;
629         char *context = NULL;
630         char *argv[2];
631         char *mygroup = NULL;
632         char *recbase = NULL;
633         int fd = 0;
634         struct ast_flags flags;
635         int oldwf = 0;
636         int argc = 0;
637         int volfactor = 0;
638         int res;
639
640         data = ast_strdupa(data);
641
642         u = ast_module_user_add(chan);
643
644         if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
645                 context = argv[0];
646                 exten = strsep(&context, "@");
647                 if (ast_strlen_zero(context))
648                         context = ast_strdupa(chan->context);
649                 if (argc > 1)
650                         options = argv[1];
651         }
652
653         if (options) {
654                 char *opts[OPT_ARG_ARRAY_SIZE];
655                 
656                 ast_app_parse_options(spy_opts, &flags, opts, options);
657                 if (ast_test_flag(&flags, OPTION_GROUP))
658                         mygroup = opts[OPT_ARG_GROUP];
659
660                 if (ast_test_flag(&flags, OPTION_RECORD) &&
661                     !(recbase = opts[OPT_ARG_RECORD]))
662                         recbase = "chanspy";
663
664                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
665                         int vol;
666
667                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
668                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
669                         else
670                                 volfactor = vol;
671                 }
672
673                 if (ast_test_flag(&flags, OPTION_PRIVATE))
674                         ast_set_flag(&flags, OPTION_WHISPER);
675         }
676
677         oldwf = chan->writeformat;
678         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
679                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
680                 ast_module_user_remove(u);
681                 return -1;
682         }
683
684         if (recbase) {
685                 char filename[512];
686
687                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
688                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
689                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
690                         fd = 0;
691                 }
692         }
693
694         res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
695
696         if (fd)
697                 close(fd);
698
699         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
700                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
701
702         ast_module_user_remove(u);
703
704         return res;
705 }
706
707 static int unload_module(void)
708 {
709         int res = 0;
710
711         res |= ast_unregister_application(app_chan);
712         res |= ast_unregister_application(app_ext);
713
714         ast_module_user_hangup_all();
715
716         return res;
717 }
718
719 static int load_module(void)
720 {
721         int res = 0;
722
723         res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
724         res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
725
726         return res;
727 }
728
729 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");