Convert the ast_channel data structure over to the astobj2 framework.
[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 - 2008, 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  * \author Joshua Colp <jcolp@digium.com>
27  * \author Russell Bryant <russell@digium.com>
28  *
29  * \ingroup applications
30  */
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <ctype.h>
37 #include <errno.h>
38
39 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/audiohook.h"
43 #include "asterisk/features.h"
44 #include "asterisk/app.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/say.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/translate.h"
49 #include "asterisk/manager.h"
50 #include "asterisk/module.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/options.h"
53 #include "asterisk/autochan.h"
54
55 #define AST_NAME_STRLEN 256
56 #define NUM_SPYGROUPS 128
57
58 /*** DOCUMENTATION
59         <application name="ChanSpy" language="en_US">
60                 <synopsis>
61                         Listen to a channel, and optionally whisper into it.
62                 </synopsis>
63                 <syntax>
64                         <parameter name="chanprefix" />
65                         <parameter name="options">
66                                 <optionlist>
67                                         <option name="b">
68                                                 <para>Only spy on channels involved in a bridged call.</para>
69                                         </option>
70                                         <option name="B">
71                                                 <para>Instead of whispering on a single channel barge in on both
72                                                 channels involved in the call.</para>
73                                         </option>
74                                         <option name="d">
75                                                 <para>Override the typical numeric DTMF functionality and instead
76                                                 use DTMF to switch between spy modes.</para>
77                                                 <enumlist>
78                                                         <enum name="4">
79                                                                 <para>spy mode</para>
80                                                         </enum>
81                                                         <enum name="5">
82                                                                 <para>whisper mode</para>
83                                                         </enum>
84                                                         <enum name="6">
85                                                                 <para>barge mode</para>
86                                                         </enum>
87                                                 </enumlist>
88                                         </option>
89                                         <option name="g">
90                                                 <argument name="grp" required="true">
91                                                         <para>Only spy on channels in which one or more of the groups
92                                                         listed in <replaceable>grp</replaceable> matches one or more groups from the
93                                                         <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
94                                                 </argument>
95                                                 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain 
96                                                 either a single group or a colon-delimited list of groups, such
97                                                 as <literal>sales:support:accounting</literal>.</para></note>
98                                         </option>
99                                         <option name="n" argsep="@">
100                                                 <para>Say the name of the person being spied on if that person has recorded
101                                                 his/her name. If a context is specified, then that voicemail context will
102                                                 be searched when retrieving the name, otherwise the <literal>default</literal> context
103                                                 be used when searching for the name (i.e. if SIP/1000 is the channel being
104                                                 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
105                                                 for the name).</para>
106                                                 <argument name="mailbox" />
107                                                 <argument name="context" />
108                                         </option>
109                                         <option name="q">
110                                                 <para>Don't play a beep when beginning to spy on a channel, or speak the
111                                                 selected channel name.</para>
112                                         </option>
113                                         <option name="r">
114                                                 <para>Record the session to the monitor spool directory. An optional base for the filename 
115                                                 may be specified. The default is <literal>chanspy</literal>.</para>
116                                                 <argument name="basename" />
117                                         </option>
118                                         <option name="s">
119                                                 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
120                                                 speaking the selected channel name.</para>
121                                         </option>
122                                         <option name="v">
123                                                 <argument name="value" />
124                                                 <para>Adjust the initial volume in the range from <literal>-4</literal> 
125                                                 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
126                                         </option>
127                                         <option name="w">
128                                                 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
129                                                 the spied-on channel.</para>
130                                         </option>
131                                         <option name="W">
132                                                 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
133                                                 talk to the spied-on channel but cannot listen to that channel.</para>
134                                         </option>
135                                         <option name="o">
136                                                 <para>Only listen to audio coming from this channel.</para>
137                                         </option>
138                                         <option name="X">
139                                                 <para>Allow the user to exit ChanSpy to a valid single digit
140                                                 numeric extension in the current context or the context
141                                                 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
142                                                 name of the last channel that was spied on will be stored
143                                                 in the <variable>SPY_CHANNEL</variable> variable.</para>
144                                         </option>
145                                         <option name="x">
146                                                 <argument name="digit" required="true">
147                                                         <para>Specify a DTMF digit that can be used to exit the application.</para>
148                                                 </argument>
149                                         </option>
150                                         <option name="c">
151                                                 <argument name="digit" required="true">
152                                                         <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
153                                                 </argument>
154                                         </option>
155                                         <option name="e">
156                                                 <argument name="ext" required="true" />
157                                                 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
158                                                 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited 
159                                                 list.</para>
160                                         </option>
161                                 </optionlist>           
162                         </parameter>
163                 </syntax>
164                 <description>
165                         <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio 
166                         coming in and "out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
167                         only channels beginning with this string will be spied upon.</para>
168                         <para>While spying, the following actions may be performed:</para>
169                         <para> - Dialing <literal>#</literal> cycles the volume level.</para>
170                         <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
171                         <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
172                         to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#' 
173                         while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden if the 'd' option
174                         is used</para>
175                         <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
176                         single digit extension exists in the correct context ChanSpy will exit to it.
177                         This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
178                 </description>
179                 <see-also>
180                         <ref type="application">ExtenSpy</ref>
181                 </see-also>
182         </application>
183         <application name="ExtenSpy" language="en_US">
184                 <synopsis>
185                         Listen to a channel, and optionally whisper into it.
186                 </synopsis>
187                 <syntax>
188                         <parameter name="exten" required="true" argsep="@">
189                                 <argument name="exten" required="true">
190                                         <para>Specify extension.</para>
191                                 </argument>
192                                 <argument name="context">
193                                         <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
194                                 </argument>
195                         </parameter>
196                         <parameter name="options">
197                                 <optionlist>
198                                         <option name="b">
199                                                 <para>Only spy on channels involved in a bridged call.</para>
200                                         </option>
201                                         <option name="B">
202                                                 <para>Instead of whispering on a single channel barge in on both
203                                                 channels involved in the call.</para>
204                                         </option>
205                                         <option name="d">
206                                                 <para>Override the typical numeric DTMF functionality and instead
207                                                 use DTMF to switch between spy modes.</para>
208                                                 <enumlist>
209                                                         <enum name="4">
210                                                                 <para>spy mode</para>
211                                                         </enum>
212                                                         <enum name="5">
213                                                                 <para>whisper mode</para>
214                                                         </enum>
215                                                         <enum name="6">
216                                                                 <para>barge mode</para>
217                                                         </enum>
218                                                 </enumlist>
219                                         </option>
220                                         <option name="g">
221                                                 <argument name="grp" required="true">
222                                                         <para>Only spy on channels in which one or more of the groups
223                                                         listed in <replaceable>grp</replaceable> matches one or more groups from the
224                                                         <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
225                                                 </argument>
226                                                 <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain 
227                                                 either a single group or a colon-delimited list of groups, such
228                                                 as <literal>sales:support:accounting</literal>.</para></note>
229                                         </option>
230                                         <option name="n" argsep="@">
231                                                 <para>Say the name of the person being spied on if that person has recorded
232                                                 his/her name. If a context is specified, then that voicemail context will
233                                                 be searched when retrieving the name, otherwise the <literal>default</literal> context
234                                                 be used when searching for the name (i.e. if SIP/1000 is the channel being
235                                                 spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
236                                                 for the name).</para>
237                                                 <argument name="mailbox" />
238                                                 <argument name="context" />
239                                         </option>
240                                         <option name="q">
241                                                 <para>Don't play a beep when beginning to spy on a channel, or speak the
242                                                 selected channel name.</para>
243                                         </option>
244                                         <option name="r">
245                                                 <para>Record the session to the monitor spool directory. An optional base for the filename 
246                                                 may be specified. The default is <literal>chanspy</literal>.</para>
247                                                 <argument name="basename" />
248                                         </option>
249                                         <option name="s">
250                                                 <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
251                                                 speaking the selected channel name.</para>
252                                         </option>
253                                         <option name="v">
254                                                 <argument name="value" />
255                                                 <para>Adjust the initial volume in the range from <literal>-4</literal> 
256                                                 to <literal>4</literal>. A negative value refers to a quieter setting.</para>
257                                         </option>
258                                         <option name="w">
259                                                 <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
260                                                 the spied-on channel.</para>
261                                         </option>
262                                         <option name="W">
263                                                 <para>Enable <literal>private whisper</literal> mode, so the spying channel can
264                                                 talk to the spied-on channel but cannot listen to that channel.</para>
265                                         </option>
266                                         <option name="o">
267                                                 <para>Only listen to audio coming from this channel.</para>
268                                         </option>
269                                         <option name="X">
270                                                 <para>Allow the user to exit ChanSpy to a valid single digit
271                                                 numeric extension in the current context or the context
272                                                 specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
273                                                 name of the last channel that was spied on will be stored
274                                                 in the <variable>SPY_CHANNEL</variable> variable.</para>
275                                         </option>
276                                         <option name="x">
277                                                 <argument name="digit" required="true">
278                                                         <para>Specify a DTMF digit that can be used to exit the application.</para>
279                                                 </argument>
280                                         </option>
281                                         <option name="c">
282                                                 <argument name="digit" required="true">
283                                                         <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
284                                                 </argument>
285                                         </option>
286                                         <option name="e">
287                                                 <argument name="ext" required="true" />
288                                                 <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
289                                                 only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited 
290                                                 list.</para>
291                                         </option>
292                                 </optionlist>   
293                         </parameter>
294                 </syntax>
295                 <description>
296                         <para>This application is used to listen to the audio from an Asterisk channel. This includes 
297                         the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
298                         specified extension will be selected for spying. If the optional context is not supplied, 
299                         the current channel's context will be used.</para>
300                         <para>While spying, the following actions may be performed:</para>
301                         <para> - Dialing <literal>#</literal> cycles the volume level.</para>
302                         <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
303                         <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
304                         single digit extension exists in the correct context ChanSpy will exit to it.
305                         This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
306                 </description>
307                 <see-also>
308                         <ref type="application">ChanSpy</ref>
309                 </see-also>
310         </application>
311         
312         <application name="DAHDIScan" language="en_US">
313                 <synopsis>
314                         Scan DAHDI channels to monitor calls.
315                 </synopsis>
316                 <syntax>
317                         <parameter name="group">
318                                 <para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
319                         </parameter>
320                 </syntax>
321                 <description>
322                         <para>Allows a call center manager to monitor DAHDI channels in a
323                         convenient way.  Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
324                 </description>
325         </application>
326  ***/
327
328 static const char *app_chan = "ChanSpy";
329
330 static const char *app_ext = "ExtenSpy";
331
332 static const char *app_dahdiscan = "DAHDIScan";
333
334 enum {
335         OPTION_QUIET             = (1 << 0),    /* Quiet, no announcement */
336         OPTION_BRIDGED           = (1 << 1),    /* Only look at bridged calls */
337         OPTION_VOLUME            = (1 << 2),    /* Specify initial volume */
338         OPTION_GROUP             = (1 << 3),    /* Only look at channels in group */
339         OPTION_RECORD            = (1 << 4),
340         OPTION_WHISPER           = (1 << 5),
341         OPTION_PRIVATE           = (1 << 6),    /* Private Whisper mode */
342         OPTION_READONLY          = (1 << 7),    /* Don't mix the two channels */
343         OPTION_EXIT              = (1 << 8),    /* Exit to a valid single digit extension */
344         OPTION_ENFORCED          = (1 << 9),    /* Enforced mode */
345         OPTION_NOTECH            = (1 << 10),   /* Skip technology name playback */
346         OPTION_BARGE             = (1 << 11),   /* Barge mode (whisper to both channels) */
347         OPTION_NAME              = (1 << 12),   /* Say the name of the person on whom we will spy */
348         OPTION_DTMF_SWITCH_MODES = (1 << 13),   /* Allow numeric DTMF to switch between chanspy modes */
349         OPTION_DTMF_EXIT         = (1 << 14),   /* Set DTMF to exit, added for DAHDIScan integration */
350         OPTION_DTMF_CYCLE        = (1 << 15),   /* Custom DTMF for cycling next avaliable channel, (default is '*') */
351         OPTION_DAHDI_SCAN        = (1 << 16),   /* Scan groups in DAHDIScan mode */
352 } chanspy_opt_flags;
353
354 enum {
355         OPT_ARG_VOLUME = 0,
356         OPT_ARG_GROUP,
357         OPT_ARG_RECORD,
358         OPT_ARG_ENFORCED,
359         OPT_ARG_NAME,
360         OPT_ARG_EXIT,
361         OPT_ARG_CYCLE,
362         OPT_ARG_ARRAY_SIZE,
363 } chanspy_opt_args;
364
365 AST_APP_OPTIONS(spy_opts, {
366         AST_APP_OPTION('q', OPTION_QUIET),
367         AST_APP_OPTION('b', OPTION_BRIDGED),
368         AST_APP_OPTION('B', OPTION_BARGE),
369         AST_APP_OPTION('w', OPTION_WHISPER),
370         AST_APP_OPTION('W', OPTION_PRIVATE),
371         AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
372         AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
373         AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
374         AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
375         AST_APP_OPTION('o', OPTION_READONLY),
376         AST_APP_OPTION('X', OPTION_EXIT),
377         AST_APP_OPTION('s', OPTION_NOTECH),
378         AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
379         AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
380         AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT),
381         AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
382 });
383
384 struct chanspy_translation_helper {
385         /* spy data */
386         struct ast_audiohook spy_audiohook;
387         struct ast_audiohook whisper_audiohook;
388         struct ast_audiohook bridge_whisper_audiohook;
389         int fd;
390         int volfactor;
391 };
392
393 struct spy_dtmf_options {
394         char exit;
395         char cycle;
396         char volume;
397 };
398
399 static void *spy_alloc(struct ast_channel *chan, void *data)
400 {
401         /* just store the data pointer in the channel structure */
402         return data;
403 }
404
405 static void spy_release(struct ast_channel *chan, void *data)
406 {
407         /* nothing to do */
408 }
409
410 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
411 {
412         struct chanspy_translation_helper *csth = data;
413         struct ast_frame *f = NULL;
414
415         ast_audiohook_lock(&csth->spy_audiohook);
416         if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
417                 /* Channel is already gone more than likely */
418                 ast_audiohook_unlock(&csth->spy_audiohook);
419                 return -1;
420         }
421
422         f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
423
424         ast_audiohook_unlock(&csth->spy_audiohook);
425
426         if (!f)
427                 return 0;
428
429         if (ast_write(chan, f)) {
430                 ast_frfree(f);
431                 return -1;
432         }
433
434         if (csth->fd) {
435                 if (write(csth->fd, f->data.ptr, f->datalen) < 0) {
436                         ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
437                 }
438         }
439
440         ast_frfree(f);
441
442         return 0;
443 }
444
445 static struct ast_generator spygen = {
446         .alloc = spy_alloc,
447         .release = spy_release,
448         .generate = spy_generate,
449 };
450
451 static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
452 {
453         int res = 0;
454         struct ast_channel *peer = NULL;
455
456         ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, autochan->chan->name);
457
458         res = ast_audiohook_attach(autochan->chan, audiohook);
459
460         if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
461                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
462         }
463         return res;
464 }
465
466 static void change_spy_mode(const char digit, struct ast_flags *flags)
467 {
468         if (digit == '4') {
469                 ast_clear_flag(flags, OPTION_WHISPER);
470                 ast_clear_flag(flags, OPTION_BARGE);
471         } else if (digit == '5') {
472                 ast_clear_flag(flags, OPTION_BARGE);
473                 ast_set_flag(flags, OPTION_WHISPER);
474         } else if (digit == '6') {
475                 ast_clear_flag(flags, OPTION_WHISPER);
476                 ast_set_flag(flags, OPTION_BARGE);
477         }
478 }
479
480 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
481         int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
482         char *exitcontext)
483 {
484         struct chanspy_translation_helper csth;
485         int running = 0, res, x = 0;
486         char inp[24] = {0};
487         char *name;
488         struct ast_frame *f;
489         struct ast_silence_generator *silgen = NULL;
490         struct ast_autochan *spyee_bridge_autochan = NULL;
491         const char *spyer_name;
492
493         ast_channel_lock(chan);
494         spyer_name = ast_strdupa(chan->name);
495         ast_channel_unlock(chan);
496
497         /* We now hold the channel lock on spyee */
498
499         if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) {
500                 return 0;
501         }
502
503         ast_channel_lock(spyee_autochan->chan);
504         name = ast_strdupa(spyee_autochan->chan->name);
505         ast_channel_unlock(spyee_autochan->chan);
506
507         ast_verb(2, "Spying on channel %s\n", name);
508         manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
509                         "SpyerChannel: %s\r\n"
510                         "SpyeeChannel: %s\r\n",
511                         spyer_name, name);
512
513         memset(&csth, 0, sizeof(csth));
514
515         ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
516
517         if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
518                 ast_audiohook_destroy(&csth.spy_audiohook);
519                 return 0;
520         }
521
522         ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
523         ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
524         if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
525                 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
526         }
527         if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
528                 ast_channel_lock(spyee_bridge_autochan->chan);
529                 if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
530                         ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
531                 }
532                 ast_channel_unlock(spyee_bridge_autochan->chan);
533         }
534
535         ast_channel_lock(chan);
536         ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
537         ast_channel_unlock(chan);
538
539         csth.volfactor = *volfactor;
540
541         if (csth.volfactor) {
542                 csth.spy_audiohook.options.read_volume = csth.volfactor;
543                 csth.spy_audiohook.options.write_volume = csth.volfactor;
544         }
545
546         csth.fd = fd;
547
548         if (ast_test_flag(flags, OPTION_PRIVATE))
549                 silgen = ast_channel_start_silence_generator(chan);
550         else
551                 ast_activate_generator(chan, &spygen, &csth);
552
553         /* We can no longer rely on 'spyee' being an actual channel;
554            it can be hung up and freed out from under us. However, the
555            channel destructor will put NULL into our csth.spy.chan
556            field when that happens, so that is our signal that the spyee
557            channel has gone away.
558         */
559
560         /* Note: it is very important that the ast_waitfor() be the first
561            condition in this expression, so that if we wait for some period
562            of time before receiving a frame from our spying channel, we check
563            for hangup on the spied-on channel _after_ knowing that a frame
564            has arrived, since the spied-on channel could have gone away while
565            we were waiting
566         */
567         while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
568                 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
569                         running = -1;
570                         break;
571                 }
572
573                 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
574                         ast_audiohook_lock(&csth.whisper_audiohook);
575                         ast_audiohook_lock(&csth.bridge_whisper_audiohook);
576                         ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
577                         ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
578                         ast_audiohook_unlock(&csth.whisper_audiohook);
579                         ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
580                         ast_frfree(f);
581                         continue;
582                 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
583                         ast_audiohook_lock(&csth.whisper_audiohook);
584                         ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
585                         ast_audiohook_unlock(&csth.whisper_audiohook);
586                         ast_frfree(f);
587                         continue;
588                 }
589                 
590                 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
591                 ast_frfree(f);
592                 if (!res)
593                         continue;
594
595                 if (x == sizeof(inp))
596                         x = 0;
597
598                 if (res < 0) {
599                         running = -1;
600                         break;
601                 }
602
603                 if (ast_test_flag(flags, OPTION_EXIT)) {
604                         char tmp[2];
605                         tmp[0] = res;
606                         tmp[1] = '\0';
607                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
608                                 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
609                                 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
610                                 running = -2;
611                                 break;
612                         } else {
613                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
614                         }
615                 } else if (res >= '0' && res <= '9') {
616                         if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
617                                 change_spy_mode(res, flags);
618                         } else {
619                                 inp[x++] = res;
620                         }
621                 }
622
623                 if (res == user_options->cycle) {
624                         running = 0;
625                         break;
626                 } else if (res == user_options->exit) {
627                         running = -2;
628                         break;
629                 } else if (res == user_options->volume) {
630                         if (!ast_strlen_zero(inp)) {
631                                 running = atoi(inp);
632                                 break;
633                         }
634
635                         (*volfactor)++;
636                         if (*volfactor > 4)
637                                 *volfactor = -4;
638                         ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
639
640                         csth.volfactor = *volfactor;
641                         csth.spy_audiohook.options.read_volume = csth.volfactor;
642                         csth.spy_audiohook.options.write_volume = csth.volfactor;
643                 }
644         }
645
646         if (ast_test_flag(flags, OPTION_PRIVATE))
647                 ast_channel_stop_silence_generator(chan, silgen);
648         else
649                 ast_deactivate_generator(chan);
650
651         ast_channel_lock(chan);
652         ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
653         ast_channel_unlock(chan);
654
655         ast_audiohook_lock(&csth.whisper_audiohook);
656         ast_audiohook_detach(&csth.whisper_audiohook);
657         ast_audiohook_unlock(&csth.whisper_audiohook);
658         ast_audiohook_destroy(&csth.whisper_audiohook);
659         
660         ast_audiohook_lock(&csth.bridge_whisper_audiohook);
661         ast_audiohook_detach(&csth.bridge_whisper_audiohook);
662         ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
663         ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
664
665         ast_audiohook_lock(&csth.spy_audiohook);
666         ast_audiohook_detach(&csth.spy_audiohook);
667         ast_audiohook_unlock(&csth.spy_audiohook);
668         ast_audiohook_destroy(&csth.spy_audiohook);
669
670         if (spyee_bridge_autochan) {
671                 ast_autochan_destroy(spyee_bridge_autochan);
672         }
673
674         ast_verb(2, "Done Spying on channel %s\n", name);
675         manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
676
677         return running;
678 }
679
680 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
681                 struct ast_autochan *autochan, struct ast_channel *chan)
682 {
683         struct ast_channel *next;
684         const size_t pseudo_len = strlen("DAHDI/pseudo");
685
686         if (!iter) {
687                 return NULL;
688         }
689
690 redo:
691         if (!(next = ast_channel_iterator_next(iter))) {
692                 return NULL;
693         }
694
695         if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
696                 goto redo;
697         } else if (next == chan) {
698                 goto redo;
699         }
700
701         return ast_autochan_setup(next);
702 }
703
704 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
705         int volfactor, const int fd, struct spy_dtmf_options *user_options,
706         const char *mygroup, const char *myenforced, const char *spec, const char *exten,
707         const char *context, const char *mailbox, const char *name_context)
708 {
709         char nameprefix[AST_NAME_STRLEN];
710         char peer_name[AST_NAME_STRLEN + 5];
711         char exitcontext[AST_MAX_CONTEXT] = "";
712         signed char zero_volume = 0;
713         int waitms;
714         int res;
715         char *ptr;
716         int num;
717         int num_spyed_upon = 1;
718         struct ast_channel_iterator *iter = NULL;
719
720         if (ast_test_flag(flags, OPTION_EXIT)) {
721                 const char *c;
722                 ast_channel_lock(chan);
723                 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
724                         ast_copy_string(exitcontext, c, sizeof(exitcontext));
725                 } else if (!ast_strlen_zero(chan->macrocontext)) {
726                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
727                 } else {
728                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
729                 }
730                 ast_channel_unlock(chan);
731         }
732
733         if (chan->_state != AST_STATE_UP)
734                 ast_answer(chan);
735
736         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
737
738         waitms = 100;
739
740         for (;;) {
741                 struct ast_autochan *autochan = NULL, *next_autochan = NULL;
742                 struct ast_channel *prev = NULL;
743
744                 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
745                         res = ast_streamfile(chan, "beep", chan->language);
746                         if (!res)
747                                 res = ast_waitstream(chan, "");
748                         else if (res < 0) {
749                                 ast_clear_flag(chan, AST_FLAG_SPYING);
750                                 break;
751                         }
752                         if (!ast_strlen_zero(exitcontext)) {
753                                 char tmp[2];
754                                 tmp[0] = res;
755                                 tmp[1] = '\0';
756                                 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
757                                         goto exit;
758                                 else
759                                         ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
760                         }
761                 }
762
763                 /* Set up the iterator we'll be using during this call */
764                 if (!ast_strlen_zero(spec)) {
765                         iter = ast_channel_iterator_by_name_new(0, spec, strlen(spec));
766                 } else if (!ast_strlen_zero(exten)) {
767                         iter = ast_channel_iterator_by_exten_new(0, exten, context);
768                 } else {
769                         iter = ast_channel_iterator_all_new(0);
770                 }
771
772                 if (!iter) {
773                         return -1;
774                 }
775
776                 res = ast_waitfordigit(chan, waitms);
777                 if (res < 0) {
778                         ast_clear_flag(chan, AST_FLAG_SPYING);
779                         break;
780                 }
781                 if (!ast_strlen_zero(exitcontext)) {
782                         char tmp[2];
783                         tmp[0] = res;
784                         tmp[1] = '\0';
785                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
786                                 goto exit;
787                         else
788                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
789                 }
790
791                 /* reset for the next loop around, unless overridden later */
792                 waitms = 100;
793                 num_spyed_upon = 0;
794
795                 for (autochan = next_channel(iter, autochan, chan);
796                      autochan;
797                          prev = autochan->chan, ast_autochan_destroy(autochan),
798                      autochan = next_autochan ? next_autochan : 
799                                 next_channel(iter, autochan, chan), next_autochan = NULL) {
800                         int igrp = !mygroup;
801                         int ienf = !myenforced;
802                         char *s;
803
804                         if (autochan->chan == prev) {
805                                 ast_autochan_destroy(autochan);
806                                 break;
807                         }
808
809                         if (ast_check_hangup(chan)) {
810                                 ast_autochan_destroy(autochan);
811                                 break;
812                         }
813
814                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
815                                 continue;
816                         }
817
818                         if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
819                                 continue;
820                         }
821
822                         if (mygroup) {
823                                 int num_groups = 0;
824                                 int num_mygroups = 0;
825                                 char dup_group[512];
826                                 char dup_mygroup[512];
827                                 char *groups[NUM_SPYGROUPS];
828                                 char *mygroups[NUM_SPYGROUPS];
829                                 const char *group = NULL;
830                                 int x;
831                                 int y;
832                                 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
833                                 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
834                                         ARRAY_LEN(mygroups));
835
836                                 /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable 
837                                  * rather than "SPYGROUP", this check is done to preserve expected behavior */
838                                 if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
839                                         group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
840                                 } else {
841                                         group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
842                                 }
843
844                                 if (!ast_strlen_zero(group)) {
845                                         ast_copy_string(dup_group, group, sizeof(dup_group));
846                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
847                                                 ARRAY_LEN(groups));
848                                 }
849
850                                 for (y = 0; y < num_mygroups; y++) {
851                                         for (x = 0; x < num_groups; x++) {
852                                                 if (!strcmp(mygroups[y], groups[x])) {
853                                                         igrp = 1;
854                                                         break;
855                                                 }
856                                         }
857                                 }
858                         }
859
860                         if (!igrp) {
861                                 continue;
862                         }
863                         if (myenforced) {
864                                 char ext[AST_CHANNEL_NAME + 3];
865                                 char buffer[512];
866                                 char *end;
867
868                                 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
869
870                                 ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
871                                 if ((end = strchr(ext, '-'))) {
872                                         *end++ = ':';
873                                         *end = '\0';
874                                 }
875
876                                 ext[0] = ':';
877
878                                 if (strcasestr(buffer, ext)) {
879                                         ienf = 1;
880                                 }
881                         }
882
883                         if (!ienf) {
884                                 continue;
885                         }
886
887                         strcpy(peer_name, "spy-");
888                         strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
889                         ptr = strchr(peer_name, '/');
890                         *ptr++ = '\0';
891                         ptr = strsep(&ptr, "-");
892
893                         for (s = peer_name; s < ptr; s++)
894                                 *s = tolower(*s);
895
896                         if (!ast_test_flag(flags, OPTION_QUIET)) {
897                                 if (ast_test_flag(flags, OPTION_NAME)) {
898                                         const char *local_context = S_OR(name_context, "default");
899                                         const char *local_mailbox = S_OR(mailbox, ptr);
900                                         res = ast_app_sayname(chan, local_mailbox, local_context);
901                                 }
902                                 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
903                                         if (!ast_test_flag(flags, OPTION_NOTECH)) {
904                                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
905                                                         res = ast_streamfile(chan, peer_name, chan->language);
906                                                         if (!res) {
907                                                                 res = ast_waitstream(chan, "");
908                                                         }
909                                                         if (res) {
910                                                                 ast_autochan_destroy(autochan);
911                                                                 break;
912                                                         }
913                                                 } else {
914                                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
915                                                 }
916                                         }
917                                         if ((num = atoi(ptr)))
918                                                 ast_say_digits(chan, atoi(ptr), "", chan->language);
919                                 }
920                         }
921
922                         res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
923                         num_spyed_upon++;
924
925                         if (res == -1) {
926                                 ast_autochan_destroy(autochan);
927                                 goto exit;
928                         } else if (res == -2) {
929                                 res = 0;
930                                 ast_autochan_destroy(autochan);
931                                 goto exit;
932                         } else if (res > 1 && spec) {
933                                 struct ast_channel *next;
934
935                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
936
937                                 if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
938                                         next_autochan = ast_autochan_setup(next);
939                                         next = ast_channel_unref(next);
940                                 } else {
941                                         /* stay on this channel, if it is still valid */
942                                         if (!ast_check_hangup(autochan->chan)) {
943                                                 next_autochan = ast_autochan_setup(autochan->chan);
944                                         } else {
945                                                 /* the channel is gone */
946                                                 next_autochan = NULL;
947                                         }
948                                 }
949                         }
950                 }
951
952                 iter = ast_channel_iterator_destroy(iter);
953
954                 if (res == -1 || ast_check_hangup(chan))
955                         break;
956         }
957 exit:
958
959         ast_clear_flag(chan, AST_FLAG_SPYING);
960
961         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
962
963         return res;
964 }
965
966 static int chanspy_exec(struct ast_channel *chan, void *data)
967 {
968         char *myenforced = NULL;
969         char *mygroup = NULL;
970         char *recbase = NULL;
971         int fd = 0;
972         struct ast_flags flags;
973         struct spy_dtmf_options user_options = {
974                 .cycle = '*',
975                 .volume = '#',
976                 .exit = '\0',
977         };
978         int oldwf = 0;
979         int volfactor = 0;
980         int res;
981         char *mailbox = NULL;
982         char *name_context = NULL;
983         AST_DECLARE_APP_ARGS(args,
984                 AST_APP_ARG(spec);
985                 AST_APP_ARG(options);
986         );
987         char *opts[OPT_ARG_ARRAY_SIZE];
988
989         data = ast_strdupa(data);
990         AST_STANDARD_APP_ARGS(args, data);
991
992         if (args.spec && !strcmp(args.spec, "all"))
993                 args.spec = NULL;
994
995         if (args.options) {
996                 char tmp;
997                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
998                 if (ast_test_flag(&flags, OPTION_GROUP))
999                         mygroup = opts[OPT_ARG_GROUP];
1000
1001                 if (ast_test_flag(&flags, OPTION_RECORD) &&
1002                         !(recbase = opts[OPT_ARG_RECORD]))
1003                         recbase = "chanspy";
1004
1005                 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1006                         tmp = opts[OPT_ARG_EXIT][0];
1007                         if (strchr("0123456789*#", tmp) && tmp != '\0') {
1008                                 user_options.exit = tmp;
1009                         } else {
1010                                 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
1011                         }
1012                 }
1013
1014                 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1015                         tmp = opts[OPT_ARG_CYCLE][0];
1016                         if (strchr("0123456789*#", tmp) && tmp != '\0') {
1017                                 user_options.cycle = tmp;
1018                         } else {
1019                                 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
1020                         }
1021                 }
1022
1023                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1024                         int vol;
1025
1026                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1027                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1028                         else
1029                                 volfactor = vol;
1030                 }
1031
1032                 if (ast_test_flag(&flags, OPTION_PRIVATE))
1033                         ast_set_flag(&flags, OPTION_WHISPER);
1034
1035                 if (ast_test_flag(&flags, OPTION_ENFORCED))
1036                         myenforced = opts[OPT_ARG_ENFORCED];
1037
1038                 if (ast_test_flag(&flags, OPTION_NAME)) {
1039                         if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1040                                 char *delimiter;
1041                                 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1042                                         mailbox = opts[OPT_ARG_NAME];
1043                                         *delimiter++ = '\0';
1044                                         name_context = delimiter;
1045                                 } else {
1046                                         mailbox = opts[OPT_ARG_NAME];
1047                                 }
1048                         }
1049                 }
1050         } else {
1051                 ast_clear_flag(&flags, AST_FLAGS_ALL);
1052         }
1053
1054         oldwf = chan->writeformat;
1055         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1056                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1057                 return -1;
1058         }
1059
1060         if (recbase) {
1061                 char filename[PATH_MAX];
1062
1063                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1064                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1065                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1066                         fd = 0;
1067                 }
1068         }
1069
1070         res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1071
1072         if (fd)
1073                 close(fd);
1074
1075         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1076                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1077
1078         return res;
1079 }
1080
1081 static int extenspy_exec(struct ast_channel *chan, void *data)
1082 {
1083         char *ptr, *exten = NULL;
1084         char *mygroup = NULL;
1085         char *recbase = NULL;
1086         int fd = 0;
1087         struct ast_flags flags;
1088         struct spy_dtmf_options user_options = {
1089                 .cycle = '*',
1090                 .volume = '#',
1091                 .exit = '\0',
1092         };
1093         int oldwf = 0;
1094         int volfactor = 0;
1095         int res;
1096         char *mailbox = NULL;
1097         char *name_context = NULL;
1098         AST_DECLARE_APP_ARGS(args,
1099                 AST_APP_ARG(context);
1100                 AST_APP_ARG(options);
1101         );
1102
1103         data = ast_strdupa(data);
1104
1105         AST_STANDARD_APP_ARGS(args, data);
1106         if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
1107                 exten = args.context;
1108                 *ptr++ = '\0';
1109                 args.context = ptr;
1110         }
1111
1112         if (ast_strlen_zero(args.context))
1113                 args.context = ast_strdupa(chan->context);
1114
1115         if (args.options) {
1116                 char *opts[OPT_ARG_ARRAY_SIZE];
1117                 char tmp;
1118
1119                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
1120                 if (ast_test_flag(&flags, OPTION_GROUP))
1121                         mygroup = opts[OPT_ARG_GROUP];
1122
1123                 if (ast_test_flag(&flags, OPTION_RECORD) &&
1124                         !(recbase = opts[OPT_ARG_RECORD]))
1125                         recbase = "chanspy";
1126
1127                 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1128                         tmp = opts[OPT_ARG_EXIT][0];
1129                         if (strchr("0123456789*#", tmp) && tmp != '\0') {
1130                                 user_options.exit = tmp;
1131                         } else {
1132                                 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
1133                         }
1134                 }
1135
1136                 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1137                         tmp = opts[OPT_ARG_CYCLE][0];
1138                         if (strchr("0123456789*#", tmp) && tmp != '\0') {
1139                                 user_options.cycle = tmp;
1140                         } else {
1141                                 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
1142                         }
1143                 }
1144
1145                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1146                         int vol;
1147
1148                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1149                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1150                         else
1151                                 volfactor = vol;
1152                 }
1153
1154                 if (ast_test_flag(&flags, OPTION_PRIVATE))
1155                         ast_set_flag(&flags, OPTION_WHISPER);
1156
1157                 if (ast_test_flag(&flags, OPTION_NAME)) {
1158                         if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1159                                 char *delimiter;
1160                                 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1161                                         mailbox = opts[OPT_ARG_NAME];
1162                                         *delimiter++ = '\0';
1163                                         name_context = delimiter;
1164                                 } else {
1165                                         mailbox = opts[OPT_ARG_NAME];
1166                                 }
1167                         }
1168                 }
1169
1170         } else {
1171                 ast_clear_flag(&flags, AST_FLAGS_ALL);
1172         }
1173
1174         oldwf = chan->writeformat;
1175         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1176                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1177                 return -1;
1178         }
1179
1180         if (recbase) {
1181                 char filename[PATH_MAX];
1182
1183                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1184                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1185                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1186                         fd = 0;
1187                 }
1188         }
1189
1190
1191         res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
1192
1193         if (fd)
1194                 close(fd);
1195
1196         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1197                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1198
1199         return res;
1200 }
1201
1202 static int dahdiscan_exec(struct ast_channel *chan, void *data)
1203 {
1204         const char *spec = "DAHDI";
1205         struct ast_flags flags;
1206         struct spy_dtmf_options user_options = {
1207                 .cycle = '#',
1208                 .volume = '\0',
1209                 .exit = '*',
1210         };
1211         int oldwf = 0;
1212         int res;
1213         char *mygroup = NULL;
1214
1215         ast_clear_flag(&flags, AST_FLAGS_ALL);
1216
1217         if (!ast_strlen_zero(data)) {
1218                 mygroup = ast_strdupa(data);
1219         }
1220         ast_set_flag(&flags, OPTION_DTMF_EXIT);
1221         ast_set_flag(&flags, OPTION_DTMF_CYCLE);
1222         ast_set_flag(&flags, OPTION_DAHDI_SCAN);
1223
1224         oldwf = chan->writeformat;
1225         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1226                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1227                 return -1;
1228         }
1229
1230         res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
1231
1232         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1233                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1234
1235         return res;
1236 }
1237
1238 static int unload_module(void)
1239 {
1240         int res = 0;
1241
1242         res |= ast_unregister_application(app_chan);
1243         res |= ast_unregister_application(app_ext);
1244         res |= ast_unregister_application(app_dahdiscan);
1245
1246         return res;
1247 }
1248
1249 static int load_module(void)
1250 {
1251         int res = 0;
1252
1253         res |= ast_register_application_xml(app_chan, chanspy_exec);
1254         res |= ast_register_application_xml(app_ext, extenspy_exec);
1255         res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
1256
1257         return res;
1258 }
1259
1260 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");