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