Global var cleanup - constification and removing unused vars.
[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 };
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 };
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         ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
459         res = ast_audiohook_attach(autochan->chan, audiohook);
460
461         if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
462                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
463         }
464         return res;
465 }
466
467 static void change_spy_mode(const char digit, struct ast_flags *flags)
468 {
469         if (digit == '4') {
470                 ast_clear_flag(flags, OPTION_WHISPER);
471                 ast_clear_flag(flags, OPTION_BARGE);
472         } else if (digit == '5') {
473                 ast_clear_flag(flags, OPTION_BARGE);
474                 ast_set_flag(flags, OPTION_WHISPER);
475         } else if (digit == '6') {
476                 ast_clear_flag(flags, OPTION_WHISPER);
477                 ast_set_flag(flags, OPTION_BARGE);
478         }
479 }
480
481 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
482         int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
483         char *exitcontext)
484 {
485         struct chanspy_translation_helper csth;
486         int running = 0, res, x = 0;
487         char inp[24] = {0};
488         char *name;
489         struct ast_frame *f;
490         struct ast_silence_generator *silgen = NULL;
491         struct ast_autochan *spyee_bridge_autochan = NULL;
492         const char *spyer_name;
493
494         ast_channel_lock(chan);
495         spyer_name = ast_strdupa(chan->name);
496         ast_channel_unlock(chan);
497
498         /* We now hold the channel lock on spyee */
499
500         if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) {
501                 return 0;
502         }
503
504         ast_channel_lock(spyee_autochan->chan);
505         name = ast_strdupa(spyee_autochan->chan->name);
506         ast_channel_unlock(spyee_autochan->chan);
507
508         ast_verb(2, "Spying on channel %s\n", name);
509         manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
510                         "SpyerChannel: %s\r\n"
511                         "SpyeeChannel: %s\r\n",
512                         spyer_name, name);
513
514         memset(&csth, 0, sizeof(csth));
515
516         ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
517
518         if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
519                 ast_audiohook_destroy(&csth.spy_audiohook);
520                 return 0;
521         }
522
523         ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
524         ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
525         if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
526                 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
527         }
528         if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
529                 ast_channel_lock(spyee_bridge_autochan->chan);
530                 if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
531                         ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
532                 }
533                 ast_channel_unlock(spyee_bridge_autochan->chan);
534         }
535
536         ast_channel_lock(chan);
537         ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
538         ast_channel_unlock(chan);
539
540         csth.volfactor = *volfactor;
541
542         if (csth.volfactor) {
543                 csth.spy_audiohook.options.read_volume = csth.volfactor;
544                 csth.spy_audiohook.options.write_volume = csth.volfactor;
545         }
546
547         csth.fd = fd;
548
549         if (ast_test_flag(flags, OPTION_PRIVATE))
550                 silgen = ast_channel_start_silence_generator(chan);
551         else
552                 ast_activate_generator(chan, &spygen, &csth);
553
554         /* We can no longer rely on 'spyee' being an actual channel;
555            it can be hung up and freed out from under us. However, the
556            channel destructor will put NULL into our csth.spy.chan
557            field when that happens, so that is our signal that the spyee
558            channel has gone away.
559         */
560
561         /* Note: it is very important that the ast_waitfor() be the first
562            condition in this expression, so that if we wait for some period
563            of time before receiving a frame from our spying channel, we check
564            for hangup on the spied-on channel _after_ knowing that a frame
565            has arrived, since the spied-on channel could have gone away while
566            we were waiting
567         */
568         while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
569                 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
570                         running = -1;
571                         break;
572                 }
573
574                 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
575                         ast_audiohook_lock(&csth.whisper_audiohook);
576                         ast_audiohook_lock(&csth.bridge_whisper_audiohook);
577                         ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
578                         ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
579                         ast_audiohook_unlock(&csth.whisper_audiohook);
580                         ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
581                         ast_frfree(f);
582                         continue;
583                 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
584                         ast_audiohook_lock(&csth.whisper_audiohook);
585                         ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
586                         ast_audiohook_unlock(&csth.whisper_audiohook);
587                         ast_frfree(f);
588                         continue;
589                 }
590                 
591                 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
592                 ast_frfree(f);
593                 if (!res)
594                         continue;
595
596                 if (x == sizeof(inp))
597                         x = 0;
598
599                 if (res < 0) {
600                         running = -1;
601                         break;
602                 }
603
604                 if (ast_test_flag(flags, OPTION_EXIT)) {
605                         char tmp[2];
606                         tmp[0] = res;
607                         tmp[1] = '\0';
608                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
609                                 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
610                                 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
611                                 running = -2;
612                                 break;
613                         } else {
614                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
615                         }
616                 } else if (res >= '0' && res <= '9') {
617                         if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
618                                 change_spy_mode(res, flags);
619                         } else {
620                                 inp[x++] = res;
621                         }
622                 }
623
624                 if (res == user_options->cycle) {
625                         running = 0;
626                         break;
627                 } else if (res == user_options->exit) {
628                         running = -2;
629                         break;
630                 } else if (res == user_options->volume) {
631                         if (!ast_strlen_zero(inp)) {
632                                 running = atoi(inp);
633                                 break;
634                         }
635
636                         (*volfactor)++;
637                         if (*volfactor > 4)
638                                 *volfactor = -4;
639                         ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
640
641                         csth.volfactor = *volfactor;
642                         csth.spy_audiohook.options.read_volume = csth.volfactor;
643                         csth.spy_audiohook.options.write_volume = csth.volfactor;
644                 }
645         }
646
647         if (ast_test_flag(flags, OPTION_PRIVATE))
648                 ast_channel_stop_silence_generator(chan, silgen);
649         else
650                 ast_deactivate_generator(chan);
651
652         ast_channel_lock(chan);
653         ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
654         ast_channel_unlock(chan);
655
656         ast_audiohook_lock(&csth.whisper_audiohook);
657         ast_audiohook_detach(&csth.whisper_audiohook);
658         ast_audiohook_unlock(&csth.whisper_audiohook);
659         ast_audiohook_destroy(&csth.whisper_audiohook);
660         
661         ast_audiohook_lock(&csth.bridge_whisper_audiohook);
662         ast_audiohook_detach(&csth.bridge_whisper_audiohook);
663         ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
664         ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
665
666         ast_audiohook_lock(&csth.spy_audiohook);
667         ast_audiohook_detach(&csth.spy_audiohook);
668         ast_audiohook_unlock(&csth.spy_audiohook);
669         ast_audiohook_destroy(&csth.spy_audiohook);
670
671         if (spyee_bridge_autochan) {
672                 ast_autochan_destroy(spyee_bridge_autochan);
673         }
674
675         ast_verb(2, "Done Spying on channel %s\n", name);
676         manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
677
678         return running;
679 }
680
681 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
682                 struct ast_autochan *autochan, struct ast_channel *chan)
683 {
684         struct ast_channel *next;
685         const size_t pseudo_len = strlen("DAHDI/pseudo");
686
687         if (!iter) {
688                 return NULL;
689         }
690
691 redo:
692         if (!(next = ast_channel_iterator_next(iter))) {
693                 return NULL;
694         }
695
696         if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
697                 goto redo;
698         } else if (next == chan) {
699                 goto redo;
700         }
701
702         return ast_autochan_setup(next);
703 }
704
705 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
706         int volfactor, const int fd, struct spy_dtmf_options *user_options,
707         const char *mygroup, const char *myenforced, const char *spec, const char *exten,
708         const char *context, const char *mailbox, const char *name_context)
709 {
710         char nameprefix[AST_NAME_STRLEN];
711         char peer_name[AST_NAME_STRLEN + 5];
712         char exitcontext[AST_MAX_CONTEXT] = "";
713         signed char zero_volume = 0;
714         int waitms;
715         int res;
716         char *ptr;
717         int num;
718         int num_spyed_upon = 1;
719         struct ast_channel_iterator *iter = NULL;
720
721         if (ast_test_flag(flags, OPTION_EXIT)) {
722                 const char *c;
723                 ast_channel_lock(chan);
724                 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
725                         ast_copy_string(exitcontext, c, sizeof(exitcontext));
726                 } else if (!ast_strlen_zero(chan->macrocontext)) {
727                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
728                 } else {
729                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
730                 }
731                 ast_channel_unlock(chan);
732         }
733
734         if (chan->_state != AST_STATE_UP)
735                 ast_answer(chan);
736
737         ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
738
739         waitms = 100;
740
741         for (;;) {
742                 struct ast_autochan *autochan = NULL, *next_autochan = NULL;
743                 struct ast_channel *prev = NULL;
744
745                 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
746                         res = ast_streamfile(chan, "beep", chan->language);
747                         if (!res)
748                                 res = ast_waitstream(chan, "");
749                         else if (res < 0) {
750                                 ast_clear_flag(chan, AST_FLAG_SPYING);
751                                 break;
752                         }
753                         if (!ast_strlen_zero(exitcontext)) {
754                                 char tmp[2];
755                                 tmp[0] = res;
756                                 tmp[1] = '\0';
757                                 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
758                                         goto exit;
759                                 else
760                                         ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
761                         }
762                 }
763
764                 /* Set up the iterator we'll be using during this call */
765                 if (!ast_strlen_zero(spec)) {
766                         iter = ast_channel_iterator_by_name_new(0, spec, strlen(spec));
767                 } else if (!ast_strlen_zero(exten)) {
768                         iter = ast_channel_iterator_by_exten_new(0, exten, context);
769                 } else {
770                         iter = ast_channel_iterator_all_new(0);
771                 }
772
773                 if (!iter) {
774                         return -1;
775                 }
776
777                 res = ast_waitfordigit(chan, waitms);
778                 if (res < 0) {
779                         ast_clear_flag(chan, AST_FLAG_SPYING);
780                         break;
781                 }
782                 if (!ast_strlen_zero(exitcontext)) {
783                         char tmp[2];
784                         tmp[0] = res;
785                         tmp[1] = '\0';
786                         if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
787                                 goto exit;
788                         else
789                                 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
790                 }
791
792                 /* reset for the next loop around, unless overridden later */
793                 waitms = 100;
794                 num_spyed_upon = 0;
795
796                 for (autochan = next_channel(iter, autochan, chan);
797                      autochan;
798                          prev = autochan->chan, ast_autochan_destroy(autochan),
799                      autochan = next_autochan ? next_autochan : 
800                                 next_channel(iter, autochan, chan), next_autochan = NULL) {
801                         int igrp = !mygroup;
802                         int ienf = !myenforced;
803                         char *s;
804
805                         if (autochan->chan == prev) {
806                                 ast_autochan_destroy(autochan);
807                                 break;
808                         }
809
810                         if (ast_check_hangup(chan)) {
811                                 ast_autochan_destroy(autochan);
812                                 break;
813                         }
814
815                         if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
816                                 continue;
817                         }
818
819                         if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
820                                 continue;
821                         }
822
823                         if (mygroup) {
824                                 int num_groups = 0;
825                                 int num_mygroups = 0;
826                                 char dup_group[512];
827                                 char dup_mygroup[512];
828                                 char *groups[NUM_SPYGROUPS];
829                                 char *mygroups[NUM_SPYGROUPS];
830                                 const char *group = NULL;
831                                 int x;
832                                 int y;
833                                 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
834                                 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
835                                         ARRAY_LEN(mygroups));
836
837                                 /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable 
838                                  * rather than "SPYGROUP", this check is done to preserve expected behavior */
839                                 if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
840                                         group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
841                                 } else {
842                                         group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
843                                 }
844
845                                 if (!ast_strlen_zero(group)) {
846                                         ast_copy_string(dup_group, group, sizeof(dup_group));
847                                         num_groups = ast_app_separate_args(dup_group, ':', groups,
848                                                 ARRAY_LEN(groups));
849                                 }
850
851                                 for (y = 0; y < num_mygroups; y++) {
852                                         for (x = 0; x < num_groups; x++) {
853                                                 if (!strcmp(mygroups[y], groups[x])) {
854                                                         igrp = 1;
855                                                         break;
856                                                 }
857                                         }
858                                 }
859                         }
860
861                         if (!igrp) {
862                                 continue;
863                         }
864                         if (myenforced) {
865                                 char ext[AST_CHANNEL_NAME + 3];
866                                 char buffer[512];
867                                 char *end;
868
869                                 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
870
871                                 ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
872                                 if ((end = strchr(ext, '-'))) {
873                                         *end++ = ':';
874                                         *end = '\0';
875                                 }
876
877                                 ext[0] = ':';
878
879                                 if (strcasestr(buffer, ext)) {
880                                         ienf = 1;
881                                 }
882                         }
883
884                         if (!ienf) {
885                                 continue;
886                         }
887
888                         strcpy(peer_name, "spy-");
889                         strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
890                         ptr = strchr(peer_name, '/');
891                         *ptr++ = '\0';
892                         ptr = strsep(&ptr, "-");
893
894                         for (s = peer_name; s < ptr; s++)
895                                 *s = tolower(*s);
896
897                         if (!ast_test_flag(flags, OPTION_QUIET)) {
898                                 if (ast_test_flag(flags, OPTION_NAME)) {
899                                         const char *local_context = S_OR(name_context, "default");
900                                         const char *local_mailbox = S_OR(mailbox, ptr);
901                                         res = ast_app_sayname(chan, local_mailbox, local_context);
902                                 }
903                                 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
904                                         if (!ast_test_flag(flags, OPTION_NOTECH)) {
905                                                 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
906                                                         res = ast_streamfile(chan, peer_name, chan->language);
907                                                         if (!res) {
908                                                                 res = ast_waitstream(chan, "");
909                                                         }
910                                                         if (res) {
911                                                                 ast_autochan_destroy(autochan);
912                                                                 break;
913                                                         }
914                                                 } else {
915                                                         res = ast_say_character_str(chan, peer_name, "", chan->language);
916                                                 }
917                                         }
918                                         if ((num = atoi(ptr)))
919                                                 ast_say_digits(chan, atoi(ptr), "", chan->language);
920                                 }
921                         }
922
923                         res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
924                         num_spyed_upon++;
925
926                         if (res == -1) {
927                                 ast_autochan_destroy(autochan);
928                                 goto exit;
929                         } else if (res == -2) {
930                                 res = 0;
931                                 ast_autochan_destroy(autochan);
932                                 goto exit;
933                         } else if (res > 1 && spec) {
934                                 struct ast_channel *next;
935
936                                 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
937
938                                 if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
939                                         next_autochan = ast_autochan_setup(next);
940                                         next = ast_channel_unref(next);
941                                 } else {
942                                         /* stay on this channel, if it is still valid */
943                                         if (!ast_check_hangup(autochan->chan)) {
944                                                 next_autochan = ast_autochan_setup(autochan->chan);
945                                         } else {
946                                                 /* the channel is gone */
947                                                 next_autochan = NULL;
948                                         }
949                                 }
950                         }
951                 }
952
953                 iter = ast_channel_iterator_destroy(iter);
954
955                 if (res == -1 || ast_check_hangup(chan))
956                         break;
957         }
958 exit:
959
960         ast_clear_flag(chan, AST_FLAG_SPYING);
961
962         ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
963
964         return res;
965 }
966
967 static int chanspy_exec(struct ast_channel *chan, const char *data)
968 {
969         char *myenforced = NULL;
970         char *mygroup = NULL;
971         char *recbase = NULL;
972         int fd = 0;
973         struct ast_flags flags;
974         struct spy_dtmf_options user_options = {
975                 .cycle = '*',
976                 .volume = '#',
977                 .exit = '\0',
978         };
979         int oldwf = 0;
980         int volfactor = 0;
981         int res;
982         char *mailbox = NULL;
983         char *name_context = NULL;
984         AST_DECLARE_APP_ARGS(args,
985                 AST_APP_ARG(spec);
986                 AST_APP_ARG(options);
987         );
988         char *opts[OPT_ARG_ARRAY_SIZE];
989         char *parse = ast_strdupa(data);
990
991         AST_STANDARD_APP_ARGS(args, parse);
992
993         if (args.spec && !strcmp(args.spec, "all"))
994                 args.spec = NULL;
995
996         if (args.options) {
997                 char tmp;
998                 ast_app_parse_options(spy_opts, &flags, opts, args.options);
999                 if (ast_test_flag(&flags, OPTION_GROUP))
1000                         mygroup = opts[OPT_ARG_GROUP];
1001
1002                 if (ast_test_flag(&flags, OPTION_RECORD) &&
1003                         !(recbase = opts[OPT_ARG_RECORD]))
1004                         recbase = "chanspy";
1005
1006                 if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
1007                         tmp = opts[OPT_ARG_EXIT][0];
1008                         if (strchr("0123456789*#", tmp) && tmp != '\0') {
1009                                 user_options.exit = tmp;
1010                         } else {
1011                                 ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
1012                         }
1013                 }
1014
1015                 if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
1016                         tmp = opts[OPT_ARG_CYCLE][0];
1017                         if (strchr("0123456789*#", tmp) && tmp != '\0') {
1018                                 user_options.cycle = tmp;
1019                         } else {
1020                                 ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
1021                         }
1022                 }
1023
1024                 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
1025                         int vol;
1026
1027                         if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
1028                                 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
1029                         else
1030                                 volfactor = vol;
1031                 }
1032
1033                 if (ast_test_flag(&flags, OPTION_PRIVATE))
1034                         ast_set_flag(&flags, OPTION_WHISPER);
1035
1036                 if (ast_test_flag(&flags, OPTION_ENFORCED))
1037                         myenforced = opts[OPT_ARG_ENFORCED];
1038
1039                 if (ast_test_flag(&flags, OPTION_NAME)) {
1040                         if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
1041                                 char *delimiter;
1042                                 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
1043                                         mailbox = opts[OPT_ARG_NAME];
1044                                         *delimiter++ = '\0';
1045                                         name_context = delimiter;
1046                                 } else {
1047                                         mailbox = opts[OPT_ARG_NAME];
1048                                 }
1049                         }
1050                 }
1051         } else {
1052                 ast_clear_flag(&flags, AST_FLAGS_ALL);
1053         }
1054
1055         oldwf = chan->writeformat;
1056         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1057                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1058                 return -1;
1059         }
1060
1061         if (recbase) {
1062                 char filename[PATH_MAX];
1063
1064                 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
1065                 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
1066                         ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
1067                         fd = 0;
1068                 }
1069         }
1070
1071         res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
1072
1073         if (fd)
1074                 close(fd);
1075
1076         if (oldwf && ast_set_write_format(chan, oldwf) < 0)
1077                 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
1078
1079         return res;
1080 }
1081
1082 static int extenspy_exec(struct ast_channel *chan, const char *data)
1083 {
1084         char *ptr, *exten = NULL;
1085         char *mygroup = NULL;
1086         char *recbase = NULL;
1087         int fd = 0;
1088         struct ast_flags flags;
1089         struct spy_dtmf_options user_options = {
1090                 .cycle = '*',
1091                 .volume = '#',
1092                 .exit = '\0',
1093         };
1094         int oldwf = 0;
1095         int volfactor = 0;
1096         int res;
1097         char *mailbox = NULL;
1098         char *name_context = NULL;
1099         AST_DECLARE_APP_ARGS(args,
1100                 AST_APP_ARG(context);
1101                 AST_APP_ARG(options);
1102         );
1103         char *parse = ast_strdupa(data);
1104
1105         AST_STANDARD_APP_ARGS(args, parse);
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, const char *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");