Merged revisions 99923 via svnmerge from
[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         int num_spyed_upon = 1;
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) && num_spyed_upon) {
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                 num_spyed_upon = 0;
470
471                 for (peer = next_channel(peer, spec, exten, context);
472                      peer;
473                      prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
474                         const char *group;
475                         int igrp = !mygroup;
476                         char *groups[25];
477                         int num_groups = 0;
478                         char *dup_group;
479                         int x;
480                         char *s;
481                                 
482                         if (peer == prev)
483                                 break;
484
485                         if (peer == chan)
486                                 continue;
487
488                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
489                                 continue;
490
491                         if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
492                                 continue;
493
494                         if (mygroup) {
495                                 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
496                                         dup_group = ast_strdupa(group);
497                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
498                                                                            sizeof(groups) / sizeof(groups[0]));
499                                 }
500                                 
501                                 for (x = 0; x < num_groups; x++) {
502                                         if (!strcmp(mygroup, groups[x])) {
503                                                 igrp = 1;
504                                                 break;
505                                         }
506                                 }
507                         }
508                         
509                         if (!igrp)
510                                 continue;
511
512                         strcpy(peer_name, "spy-");
513                         strncat(peer_name, peer->name, AST_NAME_STRLEN);
514                         ptr = strchr(peer_name, '/');
515                         *ptr++ = '\0';
516                         
517                         for (s = peer_name; s < ptr; s++)
518                                 *s = tolower(*s);
519                         
520                         if (!ast_test_flag(flags, OPTION_QUIET)) {
521                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
522                                         res = ast_streamfile(chan, peer_name, chan->language);
523                                         if (!res)
524                                                 res = ast_waitstream(chan, "");
525                                         if (res)
526                                                 break;
527                                 } else
528                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
529                                 if ((num = atoi(ptr))) 
530                                         ast_say_digits(chan, atoi(ptr), "", chan->language);
531                         }
532                         
533                         waitms = 5000;
534                         res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
535                         num_spyed_upon++;       
536
537                         if (res == -1) {
538                                 goto exit;
539                         } else if (res == -2) {
540                                 res = 0;
541                                 goto exit;
542                         } else if (res > 1 && spec) {
543                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
544                                 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
545                                         ast_channel_unlock(next);
546                                 } else {
547                                         /* stay on this channel */
548                                         next = peer;
549                                 }
550                                 peer = NULL;
551                         }
552                 }
553         }
554 exit:
555
556         ast_clear_flag(chan, AST_FLAG_SPYING);
557
558         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
559
560         return res;
561 }
562
563 static int chanspy_exec(struct ast_channel *chan, void *data)
564 {
565         char *mygroup = NULL;
566         char *recbase = NULL;
567         int fd = 0;
568         struct ast_flags flags;
569         int oldwf = 0;
570         int volfactor = 0;
571         int res;
572         AST_DECLARE_APP_ARGS(args,
573                 AST_APP_ARG(spec);
574                 AST_APP_ARG(options);
575         );
576         char *opts[OPT_ARG_ARRAY_SIZE];
577
578         data = ast_strdupa(data);
579         AST_STANDARD_APP_ARGS(args, data);
580
581         if (args.spec && !strcmp(args.spec, "all"))
582                 args.spec = NULL;
583
584         if (args.options) {
585                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
586                 if (ast_test_flag(&flags, OPTION_GROUP))
587                         mygroup = opts[OPT_ARG_GROUP];
588
589                 if (ast_test_flag(&flags, OPTION_RECORD) &&
590                     !(recbase = opts[OPT_ARG_RECORD]))
591                         recbase = "chanspy";
592
593                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
594                         int vol;
595
596                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
597                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
598                         else
599                                 volfactor = vol;
600                 }
601
602                 if (ast_test_flag(&flags, OPTION_PRIVATE))
603                         ast_set_flag(&flags, OPTION_WHISPER);
604         } else
605                 ast_clear_flag(&flags, AST_FLAGS_ALL);
606
607         oldwf = chan->writeformat;
608         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
609                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
610                 return -1;
611         }
612
613         if (recbase) {
614                 char filename[512];
615
616                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
617                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
618                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
619                         fd = 0;
620                 }
621         }
622
623         res = common_exec(chan, &flags, volfactor, fd, mygroup, args.spec, NULL, NULL);
624
625         if (fd)
626                 close(fd);
627
628         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
629                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
630
631         return res;
632 }
633
634 static int extenspy_exec(struct ast_channel *chan, void *data)
635 {
636         char *ptr, *exten = NULL;
637         char *mygroup = NULL;
638         char *recbase = NULL;
639         int fd = 0;
640         struct ast_flags flags;
641         int oldwf = 0;
642         int volfactor = 0;
643         int res;
644         AST_DECLARE_APP_ARGS(args,
645                 AST_APP_ARG(context);
646                 AST_APP_ARG(options);
647         );
648
649         data = ast_strdupa(data);
650
651         AST_STANDARD_APP_ARGS(args, data);
652         if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
653                 exten = args.context;
654                 *ptr++ = '\0';
655                 args.context = ptr;
656         }
657
658         if (ast_strlen_zero(args.context))
659                 args.context = ast_strdupa(chan->context);
660
661         if (args.options) {
662                 char *opts[OPT_ARG_ARRAY_SIZE];
663                 
664                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
665                 if (ast_test_flag(&flags, OPTION_GROUP))
666                         mygroup = opts[OPT_ARG_GROUP];
667
668                 if (ast_test_flag(&flags, OPTION_RECORD) &&
669                     !(recbase = opts[OPT_ARG_RECORD]))
670                         recbase = "chanspy";
671
672                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
673                         int vol;
674
675                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
676                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
677                         else
678                                 volfactor = vol;
679                 }
680
681                 if (ast_test_flag(&flags, OPTION_PRIVATE))
682                         ast_set_flag(&flags, OPTION_WHISPER);
683         } else
684                 ast_clear_flag(&flags, AST_FLAGS_ALL);
685
686         oldwf = chan->writeformat;
687         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
688                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
689                 return -1;
690         }
691
692         if (recbase) {
693                 char filename[512];
694
695                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
696                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
697                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
698                         fd = 0;
699                 }
700         }
701
702         res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
703
704         if (fd)
705                 close(fd);
706
707         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
708                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
709
710         return res;
711 }
712
713 static int unload_module(void)
714 {
715         int res = 0;
716
717         res |= ast_unregister_application(app_chan);
718         res |= ast_unregister_application(app_ext);
719
720         return res;
721 }
722
723 static int load_module(void)
724 {
725         int res = 0;
726
727         res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
728         res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
729
730         return res;
731 }
732
733 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");