Mostly cleanup of documentation to substitute the pipe with the comma, but a few...
[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         ast_verb(2, "Spying on channel %s\n", name);
281
282         memset(&csth, 0, sizeof(csth));
283         ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
284         ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
285         if (!ast_test_flag(flags, OPTION_READONLY))
286                 ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
287         csth.spy.type = "ChanSpy";
288         csth.spy.status = CHANSPY_RUNNING;
289         csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
290         csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
291         ast_mutex_init(&csth.spy.lock);
292         csth.volfactor = *volfactor;
293         set_volume(chan, &csth);
294         if (csth.volfactor) {
295                 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
296                 csth.spy.read_vol_adjustment = csth.volfactor;
297                 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
298                 csth.spy.write_vol_adjustment = csth.volfactor;
299         }
300         csth.fd = fd;
301         
302         if (start_spying(spyee, chan, &csth.spy)) {
303                 ast_mutex_destroy(&csth.spy.lock);
304                 return 0;
305         }
306
307         if (ast_test_flag(flags, OPTION_WHISPER)) {
308                 struct ast_filestream *beepstream;
309                 int old_write_format = 0;
310
311                 ast_channel_whisper_start(csth.spy.chan);
312                 old_write_format = chan->writeformat;
313                 if ((beepstream = ast_openstream_full(chan, "beep", chan->language, 1))) {
314                         struct ast_frame *f;
315
316                         while ((f = ast_readframe(beepstream))) {
317                                 ast_channel_whisper_feed(csth.spy.chan, f);
318                                 ast_frfree(f);
319                         }
320
321                         ast_closestream(beepstream);
322                         chan->stream = NULL;
323                 }
324                 if (old_write_format)
325                         ast_set_write_format(chan, old_write_format);
326         }
327
328         if (ast_test_flag(flags, OPTION_PRIVATE))
329                 silgen = ast_channel_start_silence_generator(chan);
330         else
331                 ast_activate_generator(chan, &spygen, &csth);
332
333         /* We can no longer rely on 'spyee' being an actual channel;
334            it can be hung up and freed out from under us. However, the
335            channel destructor will put NULL into our csth.spy.chan
336            field when that happens, so that is our signal that the spyee
337            channel has gone away.
338         */
339
340         /* Note: it is very important that the ast_waitfor() be the first
341            condition in this expression, so that if we wait for some period
342            of time before receiving a frame from our spying channel, we check
343            for hangup on the spied-on channel _after_ knowing that a frame
344            has arrived, since the spied-on channel could have gone away while
345            we were waiting
346         */
347         while ((res = ast_waitfor(chan, -1) > -1) &&
348                csth.spy.status == CHANSPY_RUNNING &&
349                csth.spy.chan) {
350                 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
351                         running = -1;
352                         break;
353                 }
354
355                 if (ast_test_flag(flags, OPTION_WHISPER) &&
356                     (f->frametype == AST_FRAME_VOICE)) {
357                         ast_channel_whisper_feed(csth.spy.chan, f);
358                         ast_frfree(f);
359                         continue;
360                 }
361
362                 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
363                 ast_frfree(f);
364                 if (!res)
365                         continue;
366
367                 if (x == sizeof(inp))
368                         x = 0;
369
370                 if (res < 0) {
371                         running = -1;
372                         break;
373                 }
374                 
375                 if (ast_test_flag(flags, OPTION_EXIT)) {
376                         char tmp[2];
377                         tmp[0] = res;
378                         tmp[1] = '\0';
379                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
380                                 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
381                                 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
382                                 running = -2;
383                                 break;
384                         } else {
385                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
386                         }
387                 } else if (res >= '0' && res <= '9') {
388                         inp[x++] = res;
389                 }
390
391                 if (res == '*') {
392                         running = 0;
393                         break;
394                 } else if (res == '#') {
395                         if (!ast_strlen_zero(inp)) {
396                                 running = atoi(inp);
397                                 break;
398                         }
399
400                         (*volfactor)++;
401                         if (*volfactor > 4)
402                                 *volfactor = -4;
403                         ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
404                         csth.volfactor = *volfactor;
405                         set_volume(chan, &csth);
406                         if (csth.volfactor) {
407                                 ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
408                                 csth.spy.read_vol_adjustment = csth.volfactor;
409                                 ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
410                                 csth.spy.write_vol_adjustment = csth.volfactor;
411                         } else {
412                                 ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
413                                 ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
414                         }
415                 }
416         }
417
418         if (ast_test_flag(flags, OPTION_WHISPER) && csth.spy.chan)
419                 ast_channel_whisper_stop(csth.spy.chan);
420
421         if (ast_test_flag(flags, OPTION_PRIVATE))
422                 ast_channel_stop_silence_generator(chan, silgen);
423         else
424                 ast_deactivate_generator(chan);
425
426         csth.spy.status = CHANSPY_DONE;
427
428         /* If a channel still exists on our spy structure then we need to remove ourselves */
429         if (csth.spy.chan) {
430                 ast_channel_lock(csth.spy.chan);
431                 ast_channel_spy_remove(csth.spy.chan, &csth.spy);
432                 ast_channel_unlock(csth.spy.chan);
433         }
434         ast_channel_spy_free(&csth.spy);
435         
436         if (option_verbose >= 2)
437                 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
438         
439         return running;
440 }
441
442 static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
443                                         const char *exten, const char *context)
444 {
445         struct ast_channel *this;
446
447         redo:
448         if (spec)
449                 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
450         else if (exten)
451                 this = ast_walk_channel_by_exten_locked(last, exten, context);
452         else
453                 this = ast_channel_walk_locked(last);
454
455         if (this) {
456                 ast_channel_unlock(this);
457                 if (!strncmp(this->name, "Zap/pseudo", 10))
458                         goto redo;
459         }
460
461         return this;
462 }
463
464 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
465                        int volfactor, const int fd, const char *mygroup, const char *spec,
466                        const char *exten, const char *context)
467 {
468         struct ast_channel *peer, *prev, *next;
469         char nameprefix[AST_NAME_STRLEN];
470         char peer_name[AST_NAME_STRLEN + 5];
471         char exitcontext[AST_MAX_CONTEXT] = "";
472         signed char zero_volume = 0;
473         int waitms;
474         int res;
475         char *ptr;
476         int num;
477
478         if (ast_test_flag(flags, OPTION_EXIT)) {
479                 const char *c;
480                 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) 
481                         ast_copy_string(exitcontext, c, sizeof(exitcontext));
482                 else if (!ast_strlen_zero(chan->macrocontext)) 
483                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
484                 else
485                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
486         }
487
488         if (chan->_state != AST_STATE_UP)
489                 ast_answer(chan);
490
491         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
492
493         waitms = 100;
494
495         for (;;) {
496                 if (!ast_test_flag(flags, OPTION_QUIET)) {
497                         res = ast_streamfile(chan, "beep", chan->language);
498                         if (!res)
499                                 res = ast_waitstream(chan, "");
500                         else if (res < 0) {
501                                 ast_clear_flag(chan, AST_FLAG_SPYING);
502                                 break;
503                         }
504                         if (!ast_strlen_zero(exitcontext)) {
505                                 char tmp[2];
506                                 tmp[0] = res;
507                                 tmp[1] = '\0';
508                                 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
509                                         goto exit;
510                                 else
511                                         ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
512                         }
513                 }
514
515                 res = ast_waitfordigit(chan, waitms);
516                 if (res < 0) {
517                         ast_clear_flag(chan, AST_FLAG_SPYING);
518                         break;
519                 }
520                 if (!ast_strlen_zero(exitcontext)) {
521                         char tmp[2];
522                         tmp[0] = res;
523                         tmp[1] = '\0';
524                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
525                                 goto exit;
526                         else
527                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
528                 }
529                                 
530                 /* reset for the next loop around, unless overridden later */
531                 waitms = 100;
532                 peer = prev = next = NULL;
533
534                 for (peer = next_channel(peer, spec, exten, context);
535                      peer;
536                      prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
537                         const char *group;
538                         int igrp = !mygroup;
539                         char *groups[25];
540                         int num_groups = 0;
541                         char *dup_group;
542                         int x;
543                         char *s;
544                                 
545                         if (peer == prev)
546                                 break;
547
548                         if (peer == chan)
549                                 continue;
550
551                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
552                                 continue;
553
554                         if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
555                                 continue;
556
557                         if (mygroup) {
558                                 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
559                                         dup_group = ast_strdupa(group);
560                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
561                                                                            sizeof(groups) / sizeof(groups[0]));
562                                 }
563                                 
564                                 for (x = 0; x < num_groups; x++) {
565                                         if (!strcmp(mygroup, groups[x])) {
566                                                 igrp = 1;
567                                                 break;
568                                         }
569                                 }
570                         }
571                         
572                         if (!igrp)
573                                 continue;
574
575                         strcpy(peer_name, "spy-");
576                         strncat(peer_name, peer->name, AST_NAME_STRLEN);
577                         ptr = strchr(peer_name, '/');
578                         *ptr++ = '\0';
579                         
580                         for (s = peer_name; s < ptr; s++)
581                                 *s = tolower(*s);
582                         
583                         if (!ast_test_flag(flags, OPTION_QUIET)) {
584                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
585                                         res = ast_streamfile(chan, peer_name, chan->language);
586                                         if (!res)
587                                                 res = ast_waitstream(chan, "");
588                                         if (res)
589                                                 break;
590                                 } else
591                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
592                                 if ((num = atoi(ptr))) 
593                                         ast_say_digits(chan, atoi(ptr), "", chan->language);
594                         }
595                         
596                         waitms = 5000;
597                         res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
598                         
599                         if (res == -1) {
600                                 goto exit;
601                         } else if (res == -2) {
602                                 res = 0;
603                                 goto exit;
604                         } else if (res > 1 && spec) {
605                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
606                                 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
607                                         ast_channel_unlock(next);
608                                 } else {
609                                         /* stay on this channel */
610                                         next = peer;
611                                 }
612                                 peer = NULL;
613                         }
614                 }
615         }
616 exit:
617
618         ast_clear_flag(chan, AST_FLAG_SPYING);
619
620         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
621
622         return res;
623 }
624
625 static int chanspy_exec(struct ast_channel *chan, void *data)
626 {
627         char *mygroup = NULL;
628         char *recbase = NULL;
629         int fd = 0;
630         struct ast_flags flags;
631         int oldwf = 0;
632         int volfactor = 0;
633         int res;
634         AST_DECLARE_APP_ARGS(args,
635                 AST_APP_ARG(spec);
636                 AST_APP_ARG(options);
637         );
638         char *opts[OPT_ARG_ARRAY_SIZE];
639
640         data = ast_strdupa(data);
641         AST_STANDARD_APP_ARGS(args, data);
642
643         if (args.spec && !strcmp(args.spec, "all"))
644                 args.spec = NULL;
645
646         if (args.options) {
647                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
648                 if (ast_test_flag(&flags, OPTION_GROUP))
649                         mygroup = opts[OPT_ARG_GROUP];
650
651                 if (ast_test_flag(&flags, OPTION_RECORD) &&
652                     !(recbase = opts[OPT_ARG_RECORD]))
653                         recbase = "chanspy";
654
655                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
656                         int vol;
657
658                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
659                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
660                         else
661                                 volfactor = vol;
662                 }
663
664                 if (ast_test_flag(&flags, OPTION_PRIVATE))
665                         ast_set_flag(&flags, OPTION_WHISPER);
666         } else
667                 ast_clear_flag(&flags, AST_FLAGS_ALL);
668
669         oldwf = chan->writeformat;
670         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
671                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
672                 return -1;
673         }
674
675         if (recbase) {
676                 char filename[512];
677
678                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
679                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
680                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
681                         fd = 0;
682                 }
683         }
684
685         res = common_exec(chan, &flags, volfactor, fd, mygroup, args.spec, NULL, NULL);
686
687         if (fd)
688                 close(fd);
689
690         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
691                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
692
693         return res;
694 }
695
696 static int extenspy_exec(struct ast_channel *chan, void *data)
697 {
698         char *ptr, *exten = NULL;
699         char *mygroup = NULL;
700         char *recbase = NULL;
701         int fd = 0;
702         struct ast_flags flags;
703         int oldwf = 0;
704         int volfactor = 0;
705         int res;
706         AST_DECLARE_APP_ARGS(args,
707                 AST_APP_ARG(context);
708                 AST_APP_ARG(options);
709         );
710
711         data = ast_strdupa(data);
712
713         AST_STANDARD_APP_ARGS(args, data);
714         if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
715                 exten = args.context;
716                 *ptr++ = '\0';
717                 args.context = ptr;
718         }
719
720         if (ast_strlen_zero(args.context))
721                 args.context = ast_strdupa(chan->context);
722
723         if (args.options) {
724                 char *opts[OPT_ARG_ARRAY_SIZE];
725                 
726                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
727                 if (ast_test_flag(&flags, OPTION_GROUP))
728                         mygroup = opts[OPT_ARG_GROUP];
729
730                 if (ast_test_flag(&flags, OPTION_RECORD) &&
731                     !(recbase = opts[OPT_ARG_RECORD]))
732                         recbase = "chanspy";
733
734                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
735                         int vol;
736
737                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
738                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
739                         else
740                                 volfactor = vol;
741                 }
742
743                 if (ast_test_flag(&flags, OPTION_PRIVATE))
744                         ast_set_flag(&flags, OPTION_WHISPER);
745         } else
746                 ast_clear_flag(&flags, AST_FLAGS_ALL);
747
748         oldwf = chan->writeformat;
749         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
750                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
751                 return -1;
752         }
753
754         if (recbase) {
755                 char filename[512];
756
757                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
758                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
759                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
760                         fd = 0;
761                 }
762         }
763
764         res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
765
766         if (fd)
767                 close(fd);
768
769         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
770                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
771
772         return res;
773 }
774
775 static int unload_module(void)
776 {
777         int res = 0;
778
779         res |= ast_unregister_application(app_chan);
780         res |= ast_unregister_application(app_ext);
781
782         return res;
783 }
784
785 static int load_module(void)
786 {
787         int res = 0;
788
789         res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
790         res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
791
792         return res;
793 }
794
795 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");