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