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