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