include "logger.h" and errno.h from asterisk.h - usage shows that they
[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/file.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/audiohook.h"
39 #include "asterisk/features.h"
40 #include "asterisk/options.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         if (option_verbose >= 2)
373                 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
374         
375         return running;
376 }
377
378 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
379                                         const char *exten, const char *context)
380 {
381         struct ast_channel *this;
382
383         redo:
384         if (spec)
385                 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
386         else if (exten)
387                 this = ast_walk_channel_by_exten_locked(last, exten, context);
388         else
389                 this = ast_channel_walk_locked(last);
390
391         if (this) {
392                 ast_channel_unlock(this);
393                 if (!strncmp(this->name, "Zap/pseudo", 10))
394                         goto redo;
395         }
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         char exitcontext[AST_MAX_CONTEXT] = "";
408         signed char zero_volume = 0;
409         int waitms;
410         int res;
411         char *ptr;
412         int num;
413
414         if (ast_test_flag(flags, OPTION_EXIT)) {
415                 const char *c;
416                 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) 
417                         ast_copy_string(exitcontext, c, sizeof(exitcontext));
418                 else if (!ast_strlen_zero(chan->macrocontext)) 
419                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
420                 else
421                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
422         }
423
424         if (chan->_state != AST_STATE_UP)
425                 ast_answer(chan);
426
427         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
428
429         waitms = 100;
430
431         for (;;) {
432                 if (!ast_test_flag(flags, OPTION_QUIET)) {
433                         res = ast_streamfile(chan, "beep", chan->language);
434                         if (!res)
435                                 res = ast_waitstream(chan, "");
436                         else if (res < 0) {
437                                 ast_clear_flag(chan, AST_FLAG_SPYING);
438                                 break;
439                         }
440                         if (!ast_strlen_zero(exitcontext)) {
441                                 char tmp[2];
442                                 tmp[0] = res;
443                                 tmp[1] = '\0';
444                                 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
445                                         goto exit;
446                                 else
447                                         ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
448                         }
449                 }
450
451                 res = ast_waitfordigit(chan, waitms);
452                 if (res < 0) {
453                         ast_clear_flag(chan, AST_FLAG_SPYING);
454                         break;
455                 }
456                 if (!ast_strlen_zero(exitcontext)) {
457                         char tmp[2];
458                         tmp[0] = res;
459                         tmp[1] = '\0';
460                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
461                                 goto exit;
462                         else
463                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
464                 }
465                                 
466                 /* reset for the next loop around, unless overridden later */
467                 waitms = 100;
468                 peer = prev = next = NULL;
469
470                 for (peer = next_channel(peer, spec, exten, context);
471                      peer;
472                      prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
473                         const char *group;
474                         int igrp = !mygroup;
475                         char *groups[25];
476                         int num_groups = 0;
477                         char *dup_group;
478                         int x;
479                         char *s;
480                                 
481                         if (peer == prev)
482                                 break;
483
484                         if (peer == chan)
485                                 continue;
486
487                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
488                                 continue;
489
490                         if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
491                                 continue;
492
493                         if (mygroup) {
494                                 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
495                                         dup_group = ast_strdupa(group);
496                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
497                                                                            sizeof(groups) / sizeof(groups[0]));
498                                 }
499                                 
500                                 for (x = 0; x < num_groups; x++) {
501                                         if (!strcmp(mygroup, groups[x])) {
502                                                 igrp = 1;
503                                                 break;
504                                         }
505                                 }
506                         }
507                         
508                         if (!igrp)
509                                 continue;
510
511                         strcpy(peer_name, "spy-");
512                         strncat(peer_name, peer->name, AST_NAME_STRLEN);
513                         ptr = strchr(peer_name, '/');
514                         *ptr++ = '\0';
515                         
516                         for (s = peer_name; s < ptr; s++)
517                                 *s = tolower(*s);
518                         
519                         if (!ast_test_flag(flags, OPTION_QUIET)) {
520                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
521                                         res = ast_streamfile(chan, peer_name, chan->language);
522                                         if (!res)
523                                                 res = ast_waitstream(chan, "");
524                                         if (res)
525                                                 break;
526                                 } else
527                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
528                                 if ((num = atoi(ptr))) 
529                                         ast_say_digits(chan, atoi(ptr), "", chan->language);
530                         }
531                         
532                         waitms = 5000;
533                         res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
534                         
535                         if (res == -1) {
536                                 goto exit;
537                         } else if (res == -2) {
538                                 res = 0;
539                                 goto exit;
540                         } else if (res > 1 && spec) {
541                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
542                                 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
543                                         ast_channel_unlock(next);
544                                 } else {
545                                         /* stay on this channel */
546                                         next = peer;
547                                 }
548                                 peer = NULL;
549                         }
550                 }
551         }
552 exit:
553
554         ast_clear_flag(chan, AST_FLAG_SPYING);
555
556         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
557
558         return res;
559 }
560
561 static int chanspy_exec(struct ast_channel *chan, void *data)
562 {
563         char *mygroup = NULL;
564         char *recbase = NULL;
565         int fd = 0;
566         struct ast_flags flags;
567         int oldwf = 0;
568         int volfactor = 0;
569         int res;
570         AST_DECLARE_APP_ARGS(args,
571                 AST_APP_ARG(spec);
572                 AST_APP_ARG(options);
573         );
574         char *opts[OPT_ARG_ARRAY_SIZE];
575
576         data = ast_strdupa(data);
577         AST_STANDARD_APP_ARGS(args, data);
578
579         if (args.spec && !strcmp(args.spec, "all"))
580                 args.spec = NULL;
581
582         if (args.options) {
583                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
584                 if (ast_test_flag(&flags, OPTION_GROUP))
585                         mygroup = opts[OPT_ARG_GROUP];
586
587                 if (ast_test_flag(&flags, OPTION_RECORD) &&
588                     !(recbase = opts[OPT_ARG_RECORD]))
589                         recbase = "chanspy";
590
591                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
592                         int vol;
593
594                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
595                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
596                         else
597                                 volfactor = vol;
598                 }
599
600                 if (ast_test_flag(&flags, OPTION_PRIVATE))
601                         ast_set_flag(&flags, OPTION_WHISPER);
602         } else
603                 ast_clear_flag(&flags, AST_FLAGS_ALL);
604
605         oldwf = chan->writeformat;
606         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
607                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
608                 return -1;
609         }
610
611         if (recbase) {
612                 char filename[512];
613
614                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
615                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
616                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
617                         fd = 0;
618                 }
619         }
620
621         res = common_exec(chan, &flags, volfactor, fd, mygroup, args.spec, NULL, NULL);
622
623         if (fd)
624                 close(fd);
625
626         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
627                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
628
629         return res;
630 }
631
632 static int extenspy_exec(struct ast_channel *chan, void *data)
633 {
634         char *ptr, *exten = NULL;
635         char *mygroup = NULL;
636         char *recbase = NULL;
637         int fd = 0;
638         struct ast_flags flags;
639         int oldwf = 0;
640         int volfactor = 0;
641         int res;
642         AST_DECLARE_APP_ARGS(args,
643                 AST_APP_ARG(context);
644                 AST_APP_ARG(options);
645         );
646
647         data = ast_strdupa(data);
648
649         AST_STANDARD_APP_ARGS(args, data);
650         if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
651                 exten = args.context;
652                 *ptr++ = '\0';
653                 args.context = ptr;
654         }
655
656         if (ast_strlen_zero(args.context))
657                 args.context = ast_strdupa(chan->context);
658
659         if (args.options) {
660                 char *opts[OPT_ARG_ARRAY_SIZE];
661                 
662                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
663                 if (ast_test_flag(&flags, OPTION_GROUP))
664                         mygroup = opts[OPT_ARG_GROUP];
665
666                 if (ast_test_flag(&flags, OPTION_RECORD) &&
667                     !(recbase = opts[OPT_ARG_RECORD]))
668                         recbase = "chanspy";
669
670                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
671                         int vol;
672
673                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
674                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
675                         else
676                                 volfactor = vol;
677                 }
678
679                 if (ast_test_flag(&flags, OPTION_PRIVATE))
680                         ast_set_flag(&flags, OPTION_WHISPER);
681         } else
682                 ast_clear_flag(&flags, AST_FLAGS_ALL);
683
684         oldwf = chan->writeformat;
685         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
686                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
687                 return -1;
688         }
689
690         if (recbase) {
691                 char filename[512];
692
693                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
694                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
695                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
696                         fd = 0;
697                 }
698         }
699
700         res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
701
702         if (fd)
703                 close(fd);
704
705         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
706                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
707
708         return res;
709 }
710
711 static int unload_module(void)
712 {
713         int res = 0;
714
715         res |= ast_unregister_application(app_chan);
716         res |= ast_unregister_application(app_ext);
717
718         return res;
719 }
720
721 static int load_module(void)
722 {
723         int res = 0;
724
725         res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
726         res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
727
728         return res;
729 }
730
731 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");