start a CHANGES file for trunk... no need to force people to have to review commit...
[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
281                 ast_channel_whisper_start(csth.spy.chan);
282                 if ((beepstream = ast_openstream_full(chan, "beep", chan->language, 1))) {
283                         struct ast_frame *f;
284
285                         while ((f = ast_readframe(beepstream))) {
286                                 ast_channel_whisper_feed(csth.spy.chan, f);
287                                 ast_frfree(f);
288                         }
289
290                         ast_closestream(beepstream);
291                 }
292         }
293
294         if (ast_test_flag(flags, OPTION_PRIVATE))
295                 silgen = ast_channel_start_silence_generator(chan);
296         else
297                 ast_activate_generator(chan, &spygen, &csth);
298
299         /* We can no longer rely on 'spyee' being an actual channel;
300            it can be hung up and freed out from under us. However, the
301            channel destructor will put NULL into our csth.spy.chan
302            field when that happens, so that is our signal that the spyee
303            channel has gone away.
304         */
305
306         /* Note: it is very important that the ast_waitfor() be the first
307            condition in this expression, so that if we wait for some period
308            of time before receiving a frame from our spying channel, we check
309            for hangup on the spied-on channel _after_ knowing that a frame
310            has arrived, since the spied-on channel could have gone away while
311            we were waiting
312         */
313         while ((res = ast_waitfor(chan, -1) > -1) &&
314                csth.spy.status == CHANSPY_RUNNING &&
315                csth.spy.chan) {
316                 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
317                         running = -1;
318                         break;
319                 }
320
321                 if (ast_test_flag(flags, OPTION_WHISPER) &&
322                     (f->frametype == AST_FRAME_VOICE)) {
323                         ast_channel_whisper_feed(csth.spy.chan, f);
324                         ast_frfree(f);
325                         continue;
326                 }
327
328                 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
329                 ast_frfree(f);
330                 if (!res)
331                         continue;
332
333                 if (x == sizeof(inp))
334                         x = 0;
335
336                 if (res < 0) {
337                         running = -1;
338                         break;
339                 }
340
341                 if (res == '*') {
342                         running = 0;
343                         break;
344                 } else if (res == '#') {
345                         if (!ast_strlen_zero(inp)) {
346                                 running = atoi(inp);
347                                 break;
348                         }
349
350                         (*volfactor)++;
351                         if (*volfactor > 4)
352                                 *volfactor = -4;
353                         if (option_verbose > 2)
354                                 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
355                         csth.volfactor = *volfactor;
356                         set_volume(chan, &csth);
357                 } else if (res >= '0' && res <= '9') {
358                         inp[x++] = res;
359                 }
360         }
361
362         if (ast_test_flag(flags, OPTION_WHISPER) && csth.spy.chan)
363                 ast_channel_whisper_stop(csth.spy.chan);
364
365         if (ast_test_flag(flags, OPTION_PRIVATE))
366                 ast_channel_stop_silence_generator(chan, silgen);
367         else
368                 ast_deactivate_generator(chan);
369
370         /* If a channel still exists on our spy structure then we need to remove ourselves */
371         if (csth.spy.chan) {
372                 csth.spy.status = CHANSPY_DONE;
373                 ast_channel_lock(csth.spy.chan);
374                 ast_channel_spy_remove(csth.spy.chan, &csth.spy);
375                 ast_channel_unlock(csth.spy.chan);
376         }
377         ast_channel_spy_free(&csth.spy);
378         
379         if (option_verbose >= 2)
380                 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
381         
382         return running;
383 }
384
385 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
386                                         const char *exten, const char *context)
387 {
388         struct ast_channel *this;
389
390         redo:
391         if (spec)
392                 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
393         else if (exten)
394                 this = ast_walk_channel_by_exten_locked(last, exten, context);
395         else
396                 this = ast_channel_walk_locked(last);
397
398         if (this) {
399                 ast_channel_unlock(this);
400                 if (!strncmp(this->name, "Zap/pseudo", 10))
401                         goto redo;
402         }
403
404         return this;
405 }
406
407 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
408                        int volfactor, const int fd, const char *mygroup, const char *spec,
409                        const char *exten, const char *context)
410 {
411         struct ast_channel *peer, *prev, *next;
412         char nameprefix[AST_NAME_STRLEN];
413         char peer_name[AST_NAME_STRLEN + 5];
414         signed char zero_volume = 0;
415         int waitms;
416         int res;
417         char *ptr;
418         int num;
419
420         if (chan->_state != AST_STATE_UP)
421                 ast_answer(chan);
422
423         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
424
425         waitms = 100;
426
427         for (;;) {
428                 if (!ast_test_flag(flags, OPTION_QUIET)) {
429                         res = ast_streamfile(chan, "beep", chan->language);
430                         if (!res)
431                                 res = ast_waitstream(chan, "");
432                         else if (res < 0) {
433                                 ast_clear_flag(chan, AST_FLAG_SPYING);
434                                 break;
435                         }
436                 }
437
438                 res = ast_waitfordigit(chan, waitms);
439                 if (res < 0) {
440                         ast_clear_flag(chan, AST_FLAG_SPYING);
441                         break;
442                 }
443                                 
444                 /* reset for the next loop around, unless overridden later */
445                 waitms = 100;
446                 peer = prev = next = NULL;
447
448                 for (peer = next_channel(peer, spec, exten, context);
449                      peer;
450                      prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
451                         const char *group;
452                         int igrp = !mygroup;
453                         char *groups[25];
454                         int num_groups = 0;
455                         char *dup_group;
456                         int x;
457                         char *s;
458                                 
459                         if (peer == prev)
460                                 break;
461
462                         if (peer == chan)
463                                 continue;
464
465                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
466                                 continue;
467
468                         if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
469                                 continue;
470
471                         if (mygroup) {
472                                 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
473                                         dup_group = ast_strdupa(group);
474                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
475                                                                            sizeof(groups) / sizeof(groups[0]));
476                                 }
477                                 
478                                 for (x = 0; x < num_groups; x++) {
479                                         if (!strcmp(mygroup, groups[x])) {
480                                                 igrp = 1;
481                                                 break;
482                                         }
483                                 }
484                         }
485                         
486                         if (!igrp)
487                                 continue;
488
489                         strcpy(peer_name, "spy-");
490                         strncat(peer_name, peer->name, AST_NAME_STRLEN);
491                         ptr = strchr(peer_name, '/');
492                         *ptr++ = '\0';
493                         
494                         for (s = peer_name; s < ptr; s++)
495                                 *s = tolower(*s);
496                         
497                         if (!ast_test_flag(flags, OPTION_QUIET)) {
498                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
499                                         res = ast_streamfile(chan, peer_name, chan->language);
500                                         if (!res)
501                                                 res = ast_waitstream(chan, "");
502                                         if (res)
503                                                 break;
504                                 } else
505                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
506                                 if ((num = atoi(ptr))) 
507                                         ast_say_digits(chan, atoi(ptr), "", chan->language);
508                         }
509                         
510                         waitms = 5000;
511                         res = channel_spy(chan, peer, &volfactor, fd, flags);
512                         
513                         if (res == -1) {
514                                 break;
515                         } else if (res > 1 && spec) {
516                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
517                                 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
518                                         ast_channel_unlock(next);
519                                 } else {
520                                         /* stay on this channel */
521                                         next = peer;
522                                 }
523                                 peer = NULL;
524                         }
525                 }
526                 if (res == -1)
527                         break;
528         }
529         
530         ast_clear_flag(chan, AST_FLAG_SPYING);
531
532         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
533
534         return res;
535 }
536
537 static int chanspy_exec(struct ast_channel *chan, void *data)
538 {
539         struct ast_module_user *u;
540         char *options = NULL;
541         char *spec = NULL;
542         char *argv[2];
543         char *mygroup = NULL;
544         char *recbase = NULL;
545         int fd = 0;
546         struct ast_flags flags;
547         int oldwf = 0;
548         int argc = 0;
549         int volfactor = 0;
550         int res;
551
552         data = ast_strdupa(data);
553
554         u = ast_module_user_add(chan);
555
556         if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
557                 spec = argv[0];
558                 if (argc > 1)
559                         options = argv[1];
560
561                 if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
562                         spec = NULL;
563         }
564
565         if (options) {
566                 char *opts[OPT_ARG_ARRAY_SIZE];
567                 
568                 ast_app_parse_options(spy_opts, &flags, opts, options);
569                 if (ast_test_flag(&flags, OPTION_GROUP))
570                         mygroup = opts[OPT_ARG_GROUP];
571
572                 if (ast_test_flag(&flags, OPTION_RECORD) &&
573                     !(recbase = opts[OPT_ARG_RECORD]))
574                         recbase = "chanspy";
575
576                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
577                         int vol;
578
579                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
580                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
581                         else
582                                 volfactor = vol;
583                 }
584
585                 if (ast_test_flag(&flags, OPTION_PRIVATE))
586                         ast_set_flag(&flags, OPTION_WHISPER);
587         }
588
589         oldwf = chan->writeformat;
590         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
591                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
592                 ast_module_user_remove(u);
593                 return -1;
594         }
595
596         if (recbase) {
597                 char filename[512];
598
599                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
600                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
601                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
602                         fd = 0;
603                 }
604         }
605
606         res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
607
608         if (fd)
609                 close(fd);
610
611         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
612                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
613
614         ast_module_user_remove(u);
615
616         return res;
617 }
618
619 static int extenspy_exec(struct ast_channel *chan, void *data)
620 {
621         struct ast_module_user *u;
622         char *options = NULL;
623         char *exten = NULL;
624         char *context = NULL;
625         char *argv[2];
626         char *mygroup = NULL;
627         char *recbase = NULL;
628         int fd = 0;
629         struct ast_flags flags;
630         int oldwf = 0;
631         int argc = 0;
632         int volfactor = 0;
633         int res;
634
635         data = ast_strdupa(data);
636
637         u = ast_module_user_add(chan);
638
639         if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
640                 context = argv[0];
641                 exten = strsep(&context, "@");
642                 if (ast_strlen_zero(context))
643                         context = ast_strdupa(chan->context);
644                 if (argc > 1)
645                         options = argv[1];
646         }
647
648         if (options) {
649                 char *opts[OPT_ARG_ARRAY_SIZE];
650                 
651                 ast_app_parse_options(spy_opts, &flags, opts, options);
652                 if (ast_test_flag(&flags, OPTION_GROUP))
653                         mygroup = opts[OPT_ARG_GROUP];
654
655                 if (ast_test_flag(&flags, OPTION_RECORD) &&
656                     !(recbase = opts[OPT_ARG_RECORD]))
657                         recbase = "chanspy";
658
659                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
660                         int vol;
661
662                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
663                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
664                         else
665                                 volfactor = vol;
666                 }
667
668                 if (ast_test_flag(&flags, OPTION_PRIVATE))
669                         ast_set_flag(&flags, OPTION_WHISPER);
670         }
671
672         oldwf = chan->writeformat;
673         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
674                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
675                 ast_module_user_remove(u);
676                 return -1;
677         }
678
679         if (recbase) {
680                 char filename[512];
681
682                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
683                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
684                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
685                         fd = 0;
686                 }
687         }
688
689         res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
690
691         if (fd)
692                 close(fd);
693
694         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
695                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
696
697         ast_module_user_remove(u);
698
699         return res;
700 }
701
702 static int unload_module(void)
703 {
704         int res = 0;
705
706         res |= ast_unregister_application(app_chan);
707         res |= ast_unregister_application(app_ext);
708
709         ast_module_user_hangup_all();
710
711         return res;
712 }
713
714 static int load_module(void)
715 {
716         int res = 0;
717
718         res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
719         res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
720
721         return res;
722 }
723
724 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");