bb1510bd27b4d7e0909e46dfc871825e86d4ed61
[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/audiohook.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 "  Note: The X option supersedes the three features above in that if a valid\n"
71 "        single digit extension exists in the correct context ChanSpy will\n"
72 "        exit to it. This also disables choosing a channel based on 'chanprefix'\n"
73 "        and a digit sequence.\n"
74 "  Options:\n"
75 "    b             - Only spy on channels involved in a bridged call.\n"
76 "    g(grp)        - Match only channels where their ${SPYGROUP} variable is set to\n"
77 "                    contain 'grp' in an optional : delimited list.\n"
78 "    q             - Don't play a beep when beginning to spy on a channel, or speak the\n"
79 "                    selected channel name.\n"
80 "    r[(basename)] - Record the session to the monitor spool directory. An\n"
81 "                    optional base for the filename may be specified. The\n"
82 "                    default is 'chanspy'.\n"
83 "    v([value])    - Adjust the initial volume in the range from -4 to 4. A\n"
84 "                    negative value refers to a quieter setting.\n"
85 "    w             - Enable 'whisper' mode, so the spying channel can talk to\n"
86 "                    the spied-on channel.\n"
87 "    W             - Enable 'private whisper' mode, so the spying channel can\n"
88 "                    talk to the spied-on channel but cannot listen to that\n"
89 "                    channel.\n"
90 "    o             - Only listen to audio coming from this channel.\n"
91 "    X             - Allow the user to exit ChanSpy to a valid single digit\n"
92 "                    numeric extension in the current context or the context\n"
93 "                    specified by the SPY_EXIT_CONTEXT channel variable. The\n"
94 "                    name of the last channel that was spied on will be stored\n"
95 "                    in the SPY_CHANNEL variable.\n"
96 ;
97
98 static const char *app_ext = "ExtenSpy";
99 static const char *desc_ext = 
100 "  ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
101 "audio from an Asterisk channel. This includes the audio coming in and\n"
102 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
103 "specified extension will be selected for spying. If the optional context is not\n"
104 "supplied, the current channel's context will be used.\n"
105 "  While spying, the following actions may be performed:\n"
106 "    - Dialing # cycles the volume level.\n"
107 "    - Dialing * will stop spying and look for another channel to spy on.\n"
108 "  Note: The X option superseeds the two features above in that if a valid\n"
109 "        single digit extension exists in the correct context it ChanSpy will\n"
110 "        exit to it.\n"
111 "  Options:\n"
112 "    b             - Only spy on channels involved in a bridged call.\n"
113 "    g(grp)        - Match only channels where their ${SPYGROUP} variable is set to\n"
114 "                    contain 'grp' in an optional : delimited list.\n"
115 "    q             - Don't play a beep when beginning to spy on a channel, or speak the\n"
116 "                    selected channel name.\n"
117 "    r[(basename)] - Record the session to the monitor spool directory. An\n"
118 "                    optional base for the filename may be specified. The\n"
119 "                    default is 'chanspy'.\n"
120 "    v([value])    - Adjust the initial volume in the range from -4 to 4. A\n"
121 "                    negative value refers to a quieter setting.\n"
122 "    w             - Enable 'whisper' mode, so the spying channel can talk to\n"
123 "                    the spied-on channel.\n"
124 "    W             - Enable 'private whisper' mode, so the spying channel can\n"
125 "                    talk to the spied-on channel but cannot listen to that\n"
126 "                    channel.\n"
127 "    o             - Only listen to audio coming from this channel.\n"
128 "    X             - Allow the user to exit ChanSpy to a valid single digit\n"
129 "                    numeric extension in the current context or the context\n"
130 "                    specified by the SPY_EXIT_CONTEXT channel variable. The\n"
131 "                    name of the last channel that was spied on will be stored\n"
132 "                    in the SPY_CHANNEL variable.\n"
133 ;
134
135 enum {
136         OPTION_QUIET     = (1 << 0),    /* Quiet, no announcement */
137         OPTION_BRIDGED   = (1 << 1),    /* Only look at bridged calls */
138         OPTION_VOLUME    = (1 << 2),    /* Specify initial volume */
139         OPTION_GROUP     = (1 << 3),    /* Only look at channels in group */
140         OPTION_RECORD    = (1 << 4),
141         OPTION_WHISPER   = (1 << 5),
142         OPTION_PRIVATE   = (1 << 6),    /* Private Whisper mode */
143         OPTION_READONLY  = (1 << 7),    /* Don't mix the two channels */
144         OPTION_EXIT      = (1 << 8),    /* Exit to a valid single digit extension */
145 } chanspy_opt_flags;
146
147 enum {
148         OPT_ARG_VOLUME = 0,
149         OPT_ARG_GROUP,
150         OPT_ARG_RECORD,
151         OPT_ARG_ARRAY_SIZE,
152 } chanspy_opt_args;
153
154 AST_APP_OPTIONS(spy_opts, {
155         AST_APP_OPTION('q', OPTION_QUIET),
156         AST_APP_OPTION('b', OPTION_BRIDGED),
157         AST_APP_OPTION('w', OPTION_WHISPER),
158         AST_APP_OPTION('W', OPTION_PRIVATE),
159         AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
160         AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
161         AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
162         AST_APP_OPTION('o', OPTION_READONLY),
163         AST_APP_OPTION('X', OPTION_EXIT),
164 });
165
166
167 struct chanspy_translation_helper {
168         /* spy data */
169         struct ast_audiohook spy_audiohook;
170         struct ast_audiohook whisper_audiohook;
171         int fd;
172         int volfactor;
173 };
174
175 static void *spy_alloc(struct ast_channel *chan, void *data)
176 {
177         /* just store the data pointer in the channel structure */
178         return data;
179 }
180
181 static void spy_release(struct ast_channel *chan, void *data)
182 {
183         /* nothing to do */
184 }
185
186 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) 
187 {
188         struct chanspy_translation_helper *csth = data;
189         struct ast_frame *f = NULL;
190                 
191         ast_audiohook_lock(&csth->spy_audiohook);
192         if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
193                 /* Channel is already gone more than likely */
194                 ast_audiohook_unlock(&csth->spy_audiohook);
195                 return -1;
196         }
197
198         f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
199
200         ast_audiohook_unlock(&csth->spy_audiohook);
201                 
202         if (!f)
203                 return 0;
204                 
205         if (ast_write(chan, f)) {
206                 ast_frfree(f);
207                 return -1;
208         }
209
210         if (csth->fd)
211                 write(csth->fd, f->data, f->datalen);
212
213         ast_frfree(f);
214
215         return 0;
216 }
217
218 static struct ast_generator spygen = {
219         .alloc = spy_alloc,
220         .release = spy_release,
221         .generate = spy_generate, 
222 };
223
224 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_audiohook *audiohook) 
225 {
226         int res = 0;
227         struct ast_channel *peer = NULL;
228
229         ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
230
231         res = ast_audiohook_attach(chan, audiohook);
232
233         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
234                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
235
236         return res;
237 }
238
239 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
240                        const struct ast_flags *flags, char *exitcontext) 
241 {
242         struct chanspy_translation_helper csth;
243         int running = 0, res, x = 0;
244         char inp[24] = {0};
245         char *name;
246         struct ast_frame *f;
247         struct ast_silence_generator *silgen = NULL;
248
249         if (ast_check_hangup(chan) || ast_check_hangup(spyee))
250                 return 0;
251
252         name = ast_strdupa(spyee->name);
253         ast_verb(2, "Spying on channel %s\n", name);
254
255         memset(&csth, 0, sizeof(csth));
256
257         ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
258         
259         if (start_spying(spyee, chan, &csth.spy_audiohook)) {
260                 ast_audiohook_destroy(&csth.spy_audiohook);
261                 return 0;
262         }
263
264         if (ast_test_flag(flags, OPTION_WHISPER)) {
265                 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
266                 start_spying(spyee, chan, &csth.whisper_audiohook);
267         }
268
269         if (ast_test_flag(flags, OPTION_PRIVATE))
270                 silgen = ast_channel_start_silence_generator(chan);
271         else
272                 ast_activate_generator(chan, &spygen, &csth);
273
274         /* We can no longer rely on 'spyee' being an actual channel;
275            it can be hung up and freed out from under us. However, the
276            channel destructor will put NULL into our csth.spy.chan
277            field when that happens, so that is our signal that the spyee
278            channel has gone away.
279         */
280
281         /* Note: it is very important that the ast_waitfor() be the first
282            condition in this expression, so that if we wait for some period
283            of time before receiving a frame from our spying channel, we check
284            for hangup on the spied-on channel _after_ knowing that a frame
285            has arrived, since the spied-on channel could have gone away while
286            we were waiting
287         */
288         while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
289                 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
290                         running = -1;
291                         break;
292                 }
293
294                 if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
295                         ast_audiohook_lock(&csth.whisper_audiohook);
296                         ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
297                         ast_audiohook_unlock(&csth.whisper_audiohook);
298                         ast_frfree(f);
299                         continue;
300                 }
301                 
302                 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
303                 ast_frfree(f);
304                 if (!res)
305                         continue;
306
307                 if (x == sizeof(inp))
308                         x = 0;
309
310                 if (res < 0) {
311                         running = -1;
312                         break;
313                 }
314                 
315                 if (ast_test_flag(flags, OPTION_EXIT)) {
316                         char tmp[2];
317                         tmp[0] = res;
318                         tmp[1] = '\0';
319                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
320                                 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
321                                 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
322                                 running = -2;
323                                 break;
324                         } else {
325                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
326                         }
327                 } else if (res >= '0' && res <= '9') {
328                         inp[x++] = res;
329                 }
330
331                 if (res == '*') {
332                         running = 0;
333                         break;
334                 } else if (res == '#') {
335                         if (!ast_strlen_zero(inp)) {
336                                 running = atoi(inp);
337                                 break;
338                         }
339
340                         (*volfactor)++;
341                         if (*volfactor > 4)
342                                 *volfactor = -4;
343                         ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
344                 }
345         }
346
347         if (ast_test_flag(flags, OPTION_PRIVATE))
348                 ast_channel_stop_silence_generator(chan, silgen);
349         else
350                 ast_deactivate_generator(chan);
351
352         if (ast_test_flag(flags, OPTION_WHISPER)) {
353                 ast_audiohook_lock(&csth.whisper_audiohook);
354                 ast_audiohook_detach(&csth.whisper_audiohook);
355                 ast_audiohook_unlock(&csth.whisper_audiohook);
356                 ast_audiohook_destroy(&csth.whisper_audiohook);
357         }
358
359         ast_audiohook_lock(&csth.spy_audiohook);
360         ast_audiohook_detach(&csth.spy_audiohook);
361         ast_audiohook_unlock(&csth.spy_audiohook);
362         ast_audiohook_destroy(&csth.spy_audiohook);
363         
364         if (option_verbose >= 2)
365                 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
366         
367         return running;
368 }
369
370 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
371                                         const char *exten, const char *context)
372 {
373         struct ast_channel *this;
374
375         redo:
376         if (spec)
377                 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
378         else if (exten)
379                 this = ast_walk_channel_by_exten_locked(last, exten, context);
380         else
381                 this = ast_channel_walk_locked(last);
382
383         if (this) {
384                 ast_channel_unlock(this);
385                 if (!strncmp(this->name, "Zap/pseudo", 10))
386                         goto redo;
387         }
388
389         return this;
390 }
391
392 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
393                        int volfactor, const int fd, const char *mygroup, const char *spec,
394                        const char *exten, const char *context)
395 {
396         struct ast_channel *peer, *prev, *next;
397         char nameprefix[AST_NAME_STRLEN];
398         char peer_name[AST_NAME_STRLEN + 5];
399         char exitcontext[AST_MAX_CONTEXT] = "";
400         signed char zero_volume = 0;
401         int waitms;
402         int res;
403         char *ptr;
404         int num;
405
406         if (ast_test_flag(flags, OPTION_EXIT)) {
407                 const char *c;
408                 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) 
409                         ast_copy_string(exitcontext, c, sizeof(exitcontext));
410                 else if (!ast_strlen_zero(chan->macrocontext)) 
411                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
412                 else
413                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
414         }
415
416         if (chan->_state != AST_STATE_UP)
417                 ast_answer(chan);
418
419         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
420
421         waitms = 100;
422
423         for (;;) {
424                 if (!ast_test_flag(flags, OPTION_QUIET)) {
425                         res = ast_streamfile(chan, "beep", chan->language);
426                         if (!res)
427                                 res = ast_waitstream(chan, "");
428                         else if (res < 0) {
429                                 ast_clear_flag(chan, AST_FLAG_SPYING);
430                                 break;
431                         }
432                         if (!ast_strlen_zero(exitcontext)) {
433                                 char tmp[2];
434                                 tmp[0] = res;
435                                 tmp[1] = '\0';
436                                 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
437                                         goto exit;
438                                 else
439                                         ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
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                 if (!ast_strlen_zero(exitcontext)) {
449                         char tmp[2];
450                         tmp[0] = res;
451                         tmp[1] = '\0';
452                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
453                                 goto exit;
454                         else
455                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
456                 }
457                                 
458                 /* reset for the next loop around, unless overridden later */
459                 waitms = 100;
460                 peer = prev = next = NULL;
461
462                 for (peer = next_channel(peer, spec, exten, context);
463                      peer;
464                      prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
465                         const char *group;
466                         int igrp = !mygroup;
467                         char *groups[25];
468                         int num_groups = 0;
469                         char *dup_group;
470                         int x;
471                         char *s;
472                                 
473                         if (peer == prev)
474                                 break;
475
476                         if (peer == chan)
477                                 continue;
478
479                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
480                                 continue;
481
482                         if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
483                                 continue;
484
485                         if (mygroup) {
486                                 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
487                                         dup_group = ast_strdupa(group);
488                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
489                                                                            sizeof(groups) / sizeof(groups[0]));
490                                 }
491                                 
492                                 for (x = 0; x < num_groups; x++) {
493                                         if (!strcmp(mygroup, groups[x])) {
494                                                 igrp = 1;
495                                                 break;
496                                         }
497                                 }
498                         }
499                         
500                         if (!igrp)
501                                 continue;
502
503                         strcpy(peer_name, "spy-");
504                         strncat(peer_name, peer->name, AST_NAME_STRLEN);
505                         ptr = strchr(peer_name, '/');
506                         *ptr++ = '\0';
507                         
508                         for (s = peer_name; s < ptr; s++)
509                                 *s = tolower(*s);
510                         
511                         if (!ast_test_flag(flags, OPTION_QUIET)) {
512                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
513                                         res = ast_streamfile(chan, peer_name, chan->language);
514                                         if (!res)
515                                                 res = ast_waitstream(chan, "");
516                                         if (res)
517                                                 break;
518                                 } else
519                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
520                                 if ((num = atoi(ptr))) 
521                                         ast_say_digits(chan, atoi(ptr), "", chan->language);
522                         }
523                         
524                         waitms = 5000;
525                         res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
526                         
527                         if (res == -1) {
528                                 goto exit;
529                         } else if (res == -2) {
530                                 res = 0;
531                                 goto exit;
532                         } else if (res > 1 && spec) {
533                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
534                                 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
535                                         ast_channel_unlock(next);
536                                 } else {
537                                         /* stay on this channel */
538                                         next = peer;
539                                 }
540                                 peer = NULL;
541                         }
542                 }
543         }
544 exit:
545
546         ast_clear_flag(chan, AST_FLAG_SPYING);
547
548         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
549
550         return res;
551 }
552
553 static int chanspy_exec(struct ast_channel *chan, void *data)
554 {
555         char *mygroup = NULL;
556         char *recbase = NULL;
557         int fd = 0;
558         struct ast_flags flags;
559         int oldwf = 0;
560         int volfactor = 0;
561         int res;
562         AST_DECLARE_APP_ARGS(args,
563                 AST_APP_ARG(spec);
564                 AST_APP_ARG(options);
565         );
566         char *opts[OPT_ARG_ARRAY_SIZE];
567
568         data = ast_strdupa(data);
569         AST_STANDARD_APP_ARGS(args, data);
570
571         if (args.spec && !strcmp(args.spec, "all"))
572                 args.spec = NULL;
573
574         if (args.options) {
575                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
576                 if (ast_test_flag(&flags, OPTION_GROUP))
577                         mygroup = opts[OPT_ARG_GROUP];
578
579                 if (ast_test_flag(&flags, OPTION_RECORD) &&
580                     !(recbase = opts[OPT_ARG_RECORD]))
581                         recbase = "chanspy";
582
583                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
584                         int vol;
585
586                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
587                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
588                         else
589                                 volfactor = vol;
590                 }
591
592                 if (ast_test_flag(&flags, OPTION_PRIVATE))
593                         ast_set_flag(&flags, OPTION_WHISPER);
594         } else
595                 ast_clear_flag(&flags, AST_FLAGS_ALL);
596
597         oldwf = chan->writeformat;
598         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
599                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
600                 return -1;
601         }
602
603         if (recbase) {
604                 char filename[512];
605
606                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
607                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
608                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
609                         fd = 0;
610                 }
611         }
612
613         res = common_exec(chan, &flags, volfactor, fd, mygroup, args.spec, NULL, NULL);
614
615         if (fd)
616                 close(fd);
617
618         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
619                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
620
621         return res;
622 }
623
624 static int extenspy_exec(struct ast_channel *chan, void *data)
625 {
626         char *ptr, *exten = NULL;
627         char *mygroup = NULL;
628         char *recbase = NULL;
629         int fd = 0;
630         struct ast_flags flags;
631         int oldwf = 0;
632         int volfactor = 0;
633         int res;
634         AST_DECLARE_APP_ARGS(args,
635                 AST_APP_ARG(context);
636                 AST_APP_ARG(options);
637         );
638
639         data = ast_strdupa(data);
640
641         AST_STANDARD_APP_ARGS(args, data);
642         if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
643                 exten = args.context;
644                 *ptr++ = '\0';
645                 args.context = ptr;
646         }
647
648         if (ast_strlen_zero(args.context))
649                 args.context = ast_strdupa(chan->context);
650
651         if (args.options) {
652                 char *opts[OPT_ARG_ARRAY_SIZE];
653                 
654                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
655                 if (ast_test_flag(&flags, OPTION_GROUP))
656                         mygroup = opts[OPT_ARG_GROUP];
657
658                 if (ast_test_flag(&flags, OPTION_RECORD) &&
659                     !(recbase = opts[OPT_ARG_RECORD]))
660                         recbase = "chanspy";
661
662                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
663                         int vol;
664
665                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
666                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
667                         else
668                                 volfactor = vol;
669                 }
670
671                 if (ast_test_flag(&flags, OPTION_PRIVATE))
672                         ast_set_flag(&flags, OPTION_WHISPER);
673         } else
674                 ast_clear_flag(&flags, AST_FLAGS_ALL);
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                 return -1;
680         }
681
682         if (recbase) {
683                 char filename[512];
684
685                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
686                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
687                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
688                         fd = 0;
689                 }
690         }
691
692         res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
693
694         if (fd)
695                 close(fd);
696
697         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
698                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
699
700         return res;
701 }
702
703 static int unload_module(void)
704 {
705         int res = 0;
706
707         res |= ast_unregister_application(app_chan);
708         res |= ast_unregister_application(app_ext);
709
710         return res;
711 }
712
713 static int load_module(void)
714 {
715         int res = 0;
716
717         res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
718         res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
719
720         return res;
721 }
722
723 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");