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