Remove required type field from channel blobs
[asterisk/asterisk.git] / apps / app_mixmonitor.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005, Anthony Minessale II
5  * Copyright (C) 2005 - 2006, Digium, Inc.
6  *
7  * Mark Spencer <markster@digium.com>
8  * Kevin P. Fleming <kpfleming@digium.com>
9  *
10  * Based on app_muxmon.c provided by
11  * Anthony Minessale II <anthmct@yahoo.com>
12  *
13  * See http://www.asterisk.org for more information about
14  * the Asterisk project. Please do not directly contact
15  * any of the maintainers of this project for assistance;
16  * the project provides a web site, mailing lists and IRC
17  * channels for your use.
18  *
19  * This program is free software, distributed under the terms of
20  * the GNU General Public License Version 2. See the LICENSE file
21  * at the top of the source tree.
22  */
23
24 /*! \file
25  *
26  * \brief MixMonitor() - Record a call and mix the audio during the recording
27  * \ingroup applications
28  *
29  * \author Mark Spencer <markster@digium.com>
30  * \author Kevin P. Fleming <kpfleming@digium.com>
31  *
32  * \note Based on app_muxmon.c provided by
33  * Anthony Minessale II <anthmct@yahoo.com>
34  */
35
36 /*** MODULEINFO
37         <support_level>core</support_level>
38  ***/
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include "asterisk/paths.h"     /* use ast_config_AST_MONITOR_DIR */
45 #include "asterisk/stringfields.h"
46 #include "asterisk/file.h"
47 #include "asterisk/audiohook.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/module.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/app.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/autochan.h"
54 #include "asterisk/manager.h"
55 #include "asterisk/callerid.h"
56 #include "asterisk/mod_format.h"
57 #include "asterisk/linkedlists.h"
58 #include "asterisk/test.h"
59
60 /*** DOCUMENTATION
61         <application name="MixMonitor" language="en_US">
62                 <synopsis>
63                         Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
64                         to guarantee the audio file is available for processing during dialplan execution.
65                 </synopsis>
66                 <syntax>
67                         <parameter name="file" required="true" argsep=".">
68                                 <argument name="filename" required="true">
69                                         <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
70                                         creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
71                                 </argument>
72                                 <argument name="extension" required="true" />
73                         </parameter>
74                         <parameter name="options">
75                                 <optionlist>
76                                         <option name="a">
77                                                 <para>Append to the file instead of overwriting it.</para>
78                                         </option>
79                                         <option name="b">
80                                                 <para>Only save audio to the file while the channel is bridged.</para>
81                                                 <note><para>Does not include conferences or sounds played to each bridged party</para></note>
82                                                 <note><para>If you utilize this option inside a Local channel, you must make sure the Local
83                                                 channel is not optimized away. To do this, be sure to call your Local channel with the
84                                                 <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
85                                         </option>
86                                         <option name="v">
87                                                 <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
88                                                 (range <literal>-4</literal> to <literal>4</literal>)</para>
89                                                 <argument name="x" required="true" />
90                                         </option>
91                                         <option name="V">
92                                                 <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
93                                                 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
94                                                 <argument name="x" required="true" />
95                                         </option>
96                                         <option name="W">
97                                                 <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
98                                                 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
99                                                 <argument name="x" required="true" />
100                                         </option>
101                                         <option name="r">
102                                                 <argument name="file" required="true" />
103                                                 <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
104                                                 Like with the basic filename argument, if an absolute path isn't given, it will create
105                                                 the file in the configured monitoring directory.</para>
106
107                                         </option>
108                                         <option name="t">
109                                                 <argument name="file" required="true" />
110                                                 <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
111                                                 Like with the basic filename argument, if an absolute path isn't given, it will create
112                                                 the file in the configured monitoring directory.</para>
113                                         </option>
114                                         <option name="i">
115                                                 <argument name="chanvar" required="true" />
116                                                 <para>Stores the MixMonitor's ID on this channel variable.</para>
117                                         </option>
118                                         <option name="m">
119                                                 <argument name="mailbox" required="true" />
120                                                 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
121                                                 separated by commas eg. m(1111@default,2222@default,...).  Folders can be optionally specified using
122                                                 the syntax: mailbox@context/folder</para>
123                                         </option>
124                                 </optionlist>
125                         </parameter>
126                         <parameter name="command">
127                                 <para>Will be executed when the recording is over.</para>
128                                 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
129                                 <para>All variables will be evaluated at the time MixMonitor is called.</para>
130                         </parameter>
131                 </syntax>
132                 <description>
133                         <para>Records the audio on the current channel to the specified file.</para>
134                         <para>This application does not automatically answer and should be preceeded by
135                         an application such as Answer or Progress().</para>
136                         <note><para>MixMonitor runs as an audiohook. In order to keep it running through
137                         a transfer, AUDIOHOOK_INHERIT must be set for the channel which ran mixmonitor.
138                         For more information, including dialplan configuration set for using
139                         AUDIOHOOK_INHERIT with MixMonitor, see the function documentation for
140                         AUDIOHOOK_INHERIT.</para></note>
141                         <variablelist>
142                                 <variable name="MIXMONITOR_FILENAME">
143                                         <para>Will contain the filename used to record.</para>
144                                 </variable>
145                         </variablelist> 
146                 </description>
147                 <see-also>
148                         <ref type="application">Monitor</ref>
149                         <ref type="application">StopMixMonitor</ref>
150                         <ref type="application">PauseMonitor</ref>
151                         <ref type="application">UnpauseMonitor</ref>
152                         <ref type="function">AUDIOHOOK_INHERIT</ref>
153                 </see-also>
154         </application>
155         <application name="StopMixMonitor" language="en_US">
156                 <synopsis>
157                         Stop recording a call through MixMonitor, and free the recording's file handle.
158                 </synopsis>
159                 <syntax>
160                         <parameter name="MixMonitorID" required="false">
161                                 <para>If a valid ID is provided, then this command will stop only that specific
162                                 MixMonitor.</para>
163                         </parameter>
164                 </syntax>
165                 <description>
166                         <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
167                         on the current channel.</para>
168                 </description>
169                 <see-also>
170                         <ref type="application">MixMonitor</ref>
171                 </see-also>
172         </application>
173         <manager name="MixMonitorMute" language="en_US">
174                 <synopsis>
175                         Mute / unMute a Mixmonitor recording.
176                 </synopsis>
177                 <syntax>
178                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
179                         <parameter name="Channel" required="true">
180                                 <para>Used to specify the channel to mute.</para>
181                         </parameter>
182                         <parameter name="Direction">
183                                 <para>Which part of the recording to mute:  read, write or both (from channel, to channel or both channels).</para>
184                         </parameter>
185                         <parameter name="State">
186                                 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
187                         </parameter>
188                 </syntax>
189                 <description>
190                         <para>This action may be used to mute a MixMonitor recording.</para>
191                 </description>
192         </manager>
193         <manager name="MixMonitor" language="en_US">
194                 <synopsis>
195                         Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
196                         to guarantee the audio file is available for processing during dialplan execution.
197                 </synopsis>
198                 <syntax>
199                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
200                         <parameter name="Channel" required="true">
201                                 <para>Used to specify the channel to record.</para>
202                         </parameter>
203                         <parameter name="File">
204                                 <para>Is the name of the file created in the monitor spool directory.
205                                 Defaults to the same name as the channel (with slashes replaced with dashes).
206                                 This argument is optional if you specify to record unidirectional audio with
207                                 either the r(filename) or t(filename) options in the options field. If
208                                 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
209                                 be recorded.</para>
210                         </parameter>
211                         <parameter name="options">
212                                 <para>Options that apply to the MixMonitor in the same way as they
213                                 would apply if invoked from the MixMonitor application. For a list of
214                                 available options, see the documentation for the mixmonitor application. </para>
215                         </parameter>
216                 </syntax>
217                 <description>
218                         <para>This action records the audio on the current channel to the specified file.</para>
219                         <variablelist>
220                                 <variable name="MIXMONITOR_FILENAME">
221                                         <para>Will contain the filename used to record the mixed stream.</para>
222                                 </variable>
223                         </variablelist>
224                 </description>
225         </manager>
226         <manager name="StopMixMonitor" language="en_US">
227                 <synopsis>
228                         Stop recording a call through MixMonitor, and free the recording's file handle.
229                 </synopsis>
230                 <syntax>
231                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
232                         <parameter name="Channel" required="true">
233                                 <para>The name of the channel monitored.</para>
234                         </parameter>
235                         <parameter name="MixMonitorID" required="false">
236                                 <para>If a valid ID is provided, then this command will stop only that specific
237                                 MixMonitor.</para>
238                         </parameter>
239                 </syntax>
240                 <description>
241                         <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
242                         action on the current channel.</para>
243                 </description>
244         </manager>
245
246  ***/
247
248 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
249
250 static const char * const app = "MixMonitor";
251
252 static const char * const stop_app = "StopMixMonitor";
253
254 static const char * const mixmonitor_spy_type = "MixMonitor";
255
256 /*!
257  * \internal
258  * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
259  */
260 struct vm_recipient {
261         char mailbox[AST_MAX_CONTEXT];
262         char context[AST_MAX_EXTENSION];
263         char folder[80];
264         AST_LIST_ENTRY(vm_recipient) list;
265 };
266
267 struct mixmonitor {
268         struct ast_audiohook audiohook;
269         struct ast_callid *callid;
270         char *filename;
271         char *filename_read;
272         char *filename_write;
273         char *post_process;
274         char *name;
275         unsigned int flags;
276         struct ast_autochan *autochan;
277         struct mixmonitor_ds *mixmonitor_ds;
278
279         /* the below string fields describe data used for creating voicemails from the recording */
280         AST_DECLARE_STRING_FIELDS(
281                 AST_STRING_FIELD(call_context);
282                 AST_STRING_FIELD(call_macrocontext);
283                 AST_STRING_FIELD(call_extension);
284                 AST_STRING_FIELD(call_callerchan);
285                 AST_STRING_FIELD(call_callerid);
286         );
287         int call_priority;
288
289         /* FUTURE DEVELOPMENT NOTICE
290          * recipient_list will need locks if we make it editable after the monitor is started */
291         AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
292 };
293
294 enum mixmonitor_flags {
295         MUXFLAG_APPEND = (1 << 1),
296         MUXFLAG_BRIDGED = (1 << 2),
297         MUXFLAG_VOLUME = (1 << 3),
298         MUXFLAG_READVOLUME = (1 << 4),
299         MUXFLAG_WRITEVOLUME = (1 << 5),
300         MUXFLAG_READ = (1 << 6),
301         MUXFLAG_WRITE = (1 << 7),
302         MUXFLAG_COMBINED = (1 << 8),
303         MUXFLAG_UID = (1 << 9),
304         MUXFLAG_VMRECIPIENTS = (1 << 10),
305 };
306
307 enum mixmonitor_args {
308         OPT_ARG_READVOLUME = 0,
309         OPT_ARG_WRITEVOLUME,
310         OPT_ARG_VOLUME,
311         OPT_ARG_WRITENAME,
312         OPT_ARG_READNAME,
313         OPT_ARG_UID,
314         OPT_ARG_VMRECIPIENTS,
315         OPT_ARG_ARRAY_SIZE,     /* Always last element of the enum */
316 };
317
318 AST_APP_OPTIONS(mixmonitor_opts, {
319         AST_APP_OPTION('a', MUXFLAG_APPEND),
320         AST_APP_OPTION('b', MUXFLAG_BRIDGED),
321         AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
322         AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
323         AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
324         AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
325         AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
326         AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
327         AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
328 });
329
330 struct mixmonitor_ds {
331         unsigned int destruction_ok;
332         ast_cond_t destruction_condition;
333         ast_mutex_t lock;
334
335         /* The filestream is held in the datastore so it can be stopped
336          * immediately during stop_mixmonitor or channel destruction. */
337         int fs_quit;
338
339         struct ast_filestream *fs;
340         struct ast_filestream *fs_read;
341         struct ast_filestream *fs_write;
342
343         struct ast_audiohook *audiohook;
344
345         unsigned int samp_rate;
346 };
347
348 /*!
349  * \internal
350  * \pre mixmonitor_ds must be locked before calling this function
351  */
352 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
353 {
354         unsigned char quitting = 0;
355
356         if (mixmonitor_ds->fs) {
357                 quitting = 1;
358                 ast_closestream(mixmonitor_ds->fs);
359                 mixmonitor_ds->fs = NULL;
360                 ast_verb(2, "MixMonitor close filestream (mixed)\n");
361         }
362
363         if (mixmonitor_ds->fs_read) {
364                 quitting = 1;
365                 ast_closestream(mixmonitor_ds->fs_read);
366                 mixmonitor_ds->fs_read = NULL;
367                 ast_verb(2, "MixMonitor close filestream (read)\n");
368         }
369
370         if (mixmonitor_ds->fs_write) {
371                 quitting = 1;
372                 ast_closestream(mixmonitor_ds->fs_write);
373                 mixmonitor_ds->fs_write = NULL;
374                 ast_verb(2, "MixMonitor close filestream (write)\n");
375         }
376
377         if (quitting) {
378                 mixmonitor_ds->fs_quit = 1;
379         }
380 }
381
382 static void mixmonitor_ds_destroy(void *data)
383 {
384         struct mixmonitor_ds *mixmonitor_ds = data;
385
386         ast_mutex_lock(&mixmonitor_ds->lock);
387         mixmonitor_ds->audiohook = NULL;
388         mixmonitor_ds->destruction_ok = 1;
389         ast_cond_signal(&mixmonitor_ds->destruction_condition);
390         ast_mutex_unlock(&mixmonitor_ds->lock);
391 }
392
393 static const struct ast_datastore_info mixmonitor_ds_info = {
394         .type = "mixmonitor",
395         .destroy = mixmonitor_ds_destroy,
396 };
397
398 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
399 {
400         if (mixmonitor->mixmonitor_ds) {
401                 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
402                 mixmonitor->mixmonitor_ds->audiohook = NULL;
403                 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
404         }
405         /* kill the audiohook.*/
406         ast_audiohook_lock(&mixmonitor->audiohook);
407         ast_audiohook_detach(&mixmonitor->audiohook);
408         ast_audiohook_unlock(&mixmonitor->audiohook);
409         ast_audiohook_destroy(&mixmonitor->audiohook);
410 }
411
412 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) 
413 {
414         struct ast_channel *peer = NULL;
415         int res = 0;
416
417         if (!chan)
418                 return -1;
419
420         ast_audiohook_attach(chan, audiohook);
421
422         if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
423                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
424
425         return res;
426 }
427
428 /*!
429  * \internal
430  * \brief adds recipients to a mixmonitor's recipient list
431  * \param mixmonitor mixmonitor being affected
432  * \param vm_recipients string containing the desired recipients to add
433  */
434 static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
435 {
436         /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
437         char *cur_mailbox = ast_strdupa(vm_recipients);
438         char *cur_context;
439         char *cur_folder;
440         char *next;
441         int elements_processed = 0;
442
443         while (!ast_strlen_zero(cur_mailbox)) {
444                 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
445                 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
446                         *(next++) = '\0';
447                 }
448
449                 if ((cur_folder = strchr(cur_mailbox, '/'))) {
450                         *(cur_folder++) = '\0';
451                 } else {
452                         cur_folder = "INBOX";
453                 }
454
455                 if ((cur_context = strchr(cur_mailbox, '@'))) {
456                         *(cur_context++) = '\0';
457                 } else {
458                         cur_context = "default";
459                 }
460
461                 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
462                         struct vm_recipient *recipient;
463                         if (!(recipient = ast_malloc(sizeof(*recipient)))) {
464                                 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
465                                 return;
466                         }
467                         ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
468                         ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
469                         ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
470
471                         /* Add to list */
472                         ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
473                         AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
474                 } else {
475                         ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
476                 }
477
478                 cur_mailbox = next;
479                 elements_processed++;
480         }
481 }
482
483 static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
484 {
485         struct vm_recipient *current;
486         while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
487                 /* Clear list element data */
488                 ast_free(current);
489         }
490 }
491
492 #define SAMPLES_PER_FRAME 160
493
494 static void mixmonitor_free(struct mixmonitor *mixmonitor)
495 {
496         if (mixmonitor) {
497                 if (mixmonitor->mixmonitor_ds) {
498                         ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
499                         ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
500                         ast_free(mixmonitor->filename_write);
501                         ast_free(mixmonitor->filename_read);
502                         ast_free(mixmonitor->mixmonitor_ds);
503                         ast_free(mixmonitor->name);
504                         ast_free(mixmonitor->post_process);
505                 }
506
507                 /* Free everything in the recipient list */
508                 clear_mixmonitor_recipient_list(mixmonitor);
509
510                 /* clean stringfields */
511                 ast_string_field_free_memory(mixmonitor);
512
513                 if (mixmonitor->callid) {
514                         ast_callid_unref(mixmonitor->callid);
515                 }
516                 ast_free(mixmonitor);
517         }
518 }
519
520 /*!
521  * \internal
522  * \brief Copies the mixmonitor to all voicemail recipients
523  * \param mixmonitor The mixmonitor that needs to forward its file to recipients
524  * \param ext Format of the file that was saved
525  */
526 static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
527 {
528         struct vm_recipient *recipient = NULL;
529         struct ast_vm_recording_data recording_data;
530         if (ast_string_field_init(&recording_data, 512)) {
531                 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
532                 return;
533         }
534
535         /* Copy strings to stringfields that will be used for all recipients */
536         ast_string_field_set(&recording_data, recording_file, filename);
537         ast_string_field_set(&recording_data, recording_ext, ext);
538         ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
539         ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
540         ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
541         ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
542         ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
543         /* and call_priority gets copied too */
544         recording_data.call_priority = mixmonitor->call_priority;
545
546         AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
547                 /* context, mailbox, and folder need to be set per recipient */
548                 ast_string_field_set(&recording_data, context, recipient->context);
549                 ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
550                 ast_string_field_set(&recording_data, folder, recipient->folder);
551
552                 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
553                         recording_data.context);
554                 ast_app_copy_recording_to_vm(&recording_data);
555         }
556
557         /* Free the string fields for recording_data before exiting the function. */
558         ast_string_field_free_memory(&recording_data);
559 }
560
561 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
562 {
563         /* Initialize the file if not already done so */
564         char *last_slash = NULL;
565         if (!ast_strlen_zero(filename)) {
566                 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
567                         *oflags = O_CREAT | O_WRONLY;
568                         *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
569
570                         last_slash = strrchr(filename, '/');
571
572                         if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
573                                 **ext = '\0';
574                                 *ext = *ext + 1;
575                         } else {
576                                 *ext = "raw";
577                         }
578
579                         if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
580                                 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
581                                 *errflag = 1;
582                         } else {
583                                 struct ast_filestream *tmp = *fs;
584                                 mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
585                         }
586                 }
587         }
588 }
589
590 static void *mixmonitor_thread(void *obj) 
591 {
592         struct mixmonitor *mixmonitor = obj;
593         char *fs_ext = "";
594         char *fs_read_ext = "";
595         char *fs_write_ext = "";
596
597         struct ast_filestream **fs = NULL;
598         struct ast_filestream **fs_read = NULL;
599         struct ast_filestream **fs_write = NULL;
600
601         unsigned int oflags;
602         int errflag = 0;
603         struct ast_format format_slin;
604
605         /* Keep callid association before any log messages */
606         if (mixmonitor->callid) {
607                 ast_callid_threadassoc_add(mixmonitor->callid);
608         }
609
610         ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
611
612         fs = &mixmonitor->mixmonitor_ds->fs;
613         fs_read = &mixmonitor->mixmonitor_ds->fs_read;
614         fs_write = &mixmonitor->mixmonitor_ds->fs_write;
615
616         ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
617         mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
618         mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
619         mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
620
621         ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
622
623         ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
624
625
626         /* The audiohook must enter and exit the loop locked */
627         ast_audiohook_lock(&mixmonitor->audiohook);
628         while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
629                 struct ast_frame *fr = NULL;
630                 struct ast_frame *fr_read = NULL;
631                 struct ast_frame *fr_write = NULL;
632
633                 if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
634                                                 &fr_read, &fr_write))) {
635                         ast_audiohook_trigger_wait(&mixmonitor->audiohook);
636
637                         if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
638                                 break;
639                         }
640                         continue;
641                 }
642
643                 /* audiohook lock is not required for the next block.
644                  * Unlock it, but remember to lock it before looping or exiting */
645                 ast_audiohook_unlock(&mixmonitor->audiohook);
646
647                 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
648                         ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
649
650                         /* Write out the frame(s) */
651                         if ((*fs_read) && (fr_read)) {
652                                 struct ast_frame *cur;
653
654                                 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
655                                         ast_writestream(*fs_read, cur);
656                                 }
657                         }
658
659                         if ((*fs_write) && (fr_write)) {
660                                 struct ast_frame *cur;
661
662                                 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
663                                         ast_writestream(*fs_write, cur);
664                                 }
665                         }
666
667                         if ((*fs) && (fr)) {
668                                 struct ast_frame *cur;
669
670                                 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
671                                         ast_writestream(*fs, cur);
672                                 }
673                         }
674                         ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
675                 }
676                 /* All done! free it. */
677                 if (fr) {
678                         ast_frame_free(fr, 0);
679                 }
680                 if (fr_read) {
681                         ast_frame_free(fr_read, 0);
682                 }
683                 if (fr_write) {
684                         ast_frame_free(fr_write, 0);
685                 }
686
687                 fr = NULL;
688                 fr_write = NULL;
689                 fr_read = NULL;
690
691                 ast_audiohook_lock(&mixmonitor->audiohook);
692         }
693
694         /* Test Event */
695         ast_test_suite_event_notify("MIXMONITOR_END", "Channel: %s\r\n"
696                                                                         "File: %s\r\n",
697                                                                         ast_channel_name(mixmonitor->autochan->chan),
698                                                                         mixmonitor->filename);
699
700         ast_audiohook_unlock(&mixmonitor->audiohook);
701
702         ast_autochan_destroy(mixmonitor->autochan);
703
704         /* Datastore cleanup.  close the filestream and wait for ds destruction */
705         ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
706         mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
707         if (!mixmonitor->mixmonitor_ds->destruction_ok) {
708                 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
709         }
710         ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
711
712         /* kill the audiohook */
713         destroy_monitor_audiohook(mixmonitor);
714
715         if (mixmonitor->post_process) {
716                 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
717                 ast_safe_system(mixmonitor->post_process);
718         }
719
720         ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
721
722         if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
723                 if (ast_strlen_zero(fs_ext)) {
724                         ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
725                                 mixmonitor -> name);
726                 } else {
727                         ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
728                         copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
729                 }
730                 if (!ast_strlen_zero(fs_read_ext)) {
731                         ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
732                         copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
733                 }
734                 if (!ast_strlen_zero(fs_write_ext)) {
735                         ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
736                         copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
737                 }
738         } else {
739                 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
740         }
741
742         mixmonitor_free(mixmonitor);
743         return NULL;
744 }
745
746 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
747 {
748         struct ast_datastore *datastore = NULL;
749         struct mixmonitor_ds *mixmonitor_ds;
750
751         if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
752                 return -1;
753         }
754
755         if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
756                 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
757         }
758
759         ast_mutex_init(&mixmonitor_ds->lock);
760         ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
761
762         if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
763                 ast_mutex_destroy(&mixmonitor_ds->lock);
764                 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
765                 ast_free(mixmonitor_ds);
766                 return -1;
767         }
768
769
770         mixmonitor_ds->samp_rate = 8000;
771         mixmonitor_ds->audiohook = &mixmonitor->audiohook;
772         datastore->data = mixmonitor_ds;
773
774         ast_channel_lock(chan);
775         ast_channel_datastore_add(chan, datastore);
776         ast_channel_unlock(chan);
777
778         mixmonitor->mixmonitor_ds = mixmonitor_ds;
779         return 0;
780 }
781
782 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
783                                   unsigned int flags, int readvol, int writevol,
784                                   const char *post_process, const char *filename_write,
785                                   char *filename_read, const char *uid_channel_var,
786                                   const char *recipients)
787 {
788         pthread_t thread;
789         struct mixmonitor *mixmonitor;
790         char postprocess2[1024] = "";
791         char *datastore_id = NULL;
792
793         postprocess2[0] = 0;
794         /* If a post process system command is given attach it to the structure */
795         if (!ast_strlen_zero(post_process)) {
796                 char *p1, *p2;
797
798                 p1 = ast_strdupa(post_process);
799                 for (p2 = p1; *p2; p2++) {
800                         if (*p2 == '^' && *(p2+1) == '{') {
801                                 *p2 = '$';
802                         }
803                 }
804                 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
805         }
806
807         /* Pre-allocate mixmonitor structure and spy */
808         if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
809                 return;
810         }
811
812         /* Now that the struct has been calloced, go ahead and initialize the string fields. */
813         if (ast_string_field_init(mixmonitor, 512)) {
814                 mixmonitor_free(mixmonitor);
815                 return;
816         }
817
818         /* Setup the actual spy before creating our thread */
819         if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
820                 mixmonitor_free(mixmonitor);
821                 return;
822         }
823
824         /* Copy over flags and channel name */
825         mixmonitor->flags = flags;
826         if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
827                 mixmonitor_free(mixmonitor);
828                 return;
829         }
830
831         if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
832                 ast_autochan_destroy(mixmonitor->autochan);
833                 mixmonitor_free(mixmonitor);
834                 ast_free(datastore_id);
835                 return;
836         }
837
838         if (!ast_strlen_zero(uid_channel_var)) {
839                 if (datastore_id) {
840                         pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
841                 }
842         }
843         ast_free(datastore_id);
844
845         mixmonitor->name = ast_strdup(ast_channel_name(chan));
846
847         if (!ast_strlen_zero(postprocess2)) {
848                 mixmonitor->post_process = ast_strdup(postprocess2);
849         }
850
851         if (!ast_strlen_zero(filename)) {
852                 mixmonitor->filename = ast_strdup(filename);
853         }
854
855         if (!ast_strlen_zero(filename_write)) {
856                 mixmonitor->filename_write = ast_strdup(filename_write);
857         }
858
859         if (!ast_strlen_zero(filename_read)) {
860                 mixmonitor->filename_read = ast_strdup(filename_read);
861         }
862
863         if (!ast_strlen_zero(recipients)) {
864                 char callerid[256];
865                 struct ast_party_connected_line *connected;
866
867                 ast_channel_lock(chan);
868
869                 /* We use the connected line of the invoking channel for caller ID. */
870
871                 connected = ast_channel_connected(chan);
872                 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
873                         connected->id.name.str, connected->id.number.valid,
874                         connected->id.number.str);
875                 ast_callerid_merge(callerid, sizeof(callerid),
876                         S_COR(connected->id.name.valid, connected->id.name.str, NULL),
877                         S_COR(connected->id.number.valid, connected->id.number.str, NULL),
878                         "Unknown");
879
880                 ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
881                 ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
882                 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
883                 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
884                 ast_string_field_set(mixmonitor, call_callerid, callerid);
885                 mixmonitor->call_priority = ast_channel_priority(chan);
886
887                 ast_channel_unlock(chan);
888
889                 add_vm_recipients_from_string(mixmonitor, recipients);
890         }
891
892         ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
893
894         if (readvol)
895                 mixmonitor->audiohook.options.read_volume = readvol;
896         if (writevol)
897                 mixmonitor->audiohook.options.write_volume = writevol;
898
899         if (startmon(chan, &mixmonitor->audiohook)) {
900                 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
901                         mixmonitor_spy_type, ast_channel_name(chan));
902                 ast_audiohook_destroy(&mixmonitor->audiohook);
903                 mixmonitor_free(mixmonitor);
904                 return;
905         }
906
907         /* reference be released at mixmonitor destruction */
908         mixmonitor->callid = ast_read_threadstorage_callid();
909
910         ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
911 }
912
913 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
914 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
915 static char *filename_parse(char *filename, char *buffer, size_t len)
916 {
917         char *slash;
918         if (ast_strlen_zero(filename)) {
919                 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
920         } else if (filename[0] != '/') {
921                 char *build;
922                 build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
923                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
924                 filename = build;
925         }
926
927         ast_copy_string(buffer, filename, len);
928
929         if ((slash = strrchr(filename, '/'))) {
930                 *slash = '\0';
931         }
932         ast_mkdir(filename, 0777);
933
934         return buffer;
935 }
936
937 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
938 {
939         int x, readvol = 0, writevol = 0;
940         char *filename_read = NULL;
941         char *filename_write = NULL;
942         char filename_buffer[1024] = "";
943         char *uid_channel_var = NULL;
944
945         struct ast_flags flags = { 0 };
946         char *recipients = NULL;
947         char *parse;
948         AST_DECLARE_APP_ARGS(args,
949                 AST_APP_ARG(filename);
950                 AST_APP_ARG(options);
951                 AST_APP_ARG(post_process);
952         );
953
954         if (ast_strlen_zero(data)) {
955                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
956                 return -1;
957         }
958
959         parse = ast_strdupa(data);
960
961         AST_STANDARD_APP_ARGS(args, parse);
962
963         if (args.options) {
964                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
965
966                 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
967
968                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
969                         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
970                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
971                         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
972                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
973                         } else {
974                                 readvol = get_volfactor(x);
975                         }
976                 }
977
978                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
979                         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
980                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
981                         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
982                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
983                         } else {
984                                 writevol = get_volfactor(x);
985                         }
986                 }
987
988                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
989                         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
990                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
991                         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
992                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
993                         } else {
994                                 readvol = writevol = get_volfactor(x);
995                         }
996                 }
997
998                 if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
999                         if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
1000                                 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1001                         } else {
1002                                 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1003                         }
1004                 }
1005
1006                 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
1007                         filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1008                 }
1009
1010                 if (ast_test_flag(&flags, MUXFLAG_READ)) {
1011                         filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1012                 }
1013
1014                 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1015                         uid_channel_var = opts[OPT_ARG_UID];
1016                 }
1017         }
1018         /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1019
1020         if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
1021                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1022                 return -1;
1023         }
1024
1025         /* If filename exists, try to create directories for it */
1026         if (!(ast_strlen_zero(args.filename))) {
1027                 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1028         }
1029
1030         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1031         launch_monitor_thread(chan,
1032                         args.filename,
1033                         flags.flags,
1034                         readvol,
1035                         writevol,
1036                         args.post_process,
1037                         filename_write,
1038                         filename_read,
1039                         uid_channel_var,
1040                         recipients);
1041
1042         return 0;
1043 }
1044
1045 static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1046 {
1047         struct ast_datastore *datastore = NULL;
1048         char *parse = "";
1049         struct mixmonitor_ds *mixmonitor_ds;
1050
1051         AST_DECLARE_APP_ARGS(args,
1052                 AST_APP_ARG(mixmonid);
1053         );
1054
1055         if (!ast_strlen_zero(data)) {
1056                 parse = ast_strdupa(data);
1057         }
1058
1059         AST_STANDARD_APP_ARGS(args, parse);
1060
1061         ast_channel_lock(chan);
1062
1063         if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
1064                 ast_channel_unlock(chan);
1065                 return -1;
1066         }
1067         mixmonitor_ds = datastore->data;
1068
1069         ast_mutex_lock(&mixmonitor_ds->lock);
1070
1071         /* closing the filestream here guarantees the file is avaliable to the dialplan
1072          * after calling StopMixMonitor */
1073         mixmonitor_ds_close_fs(mixmonitor_ds);
1074
1075         /* The mixmonitor thread may be waiting on the audiohook trigger.
1076          * In order to exit from the mixmonitor loop before waiting on channel
1077          * destruction, poke the audiohook trigger. */
1078         if (mixmonitor_ds->audiohook) {
1079                 if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
1080                         ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
1081                 }
1082                 ast_audiohook_lock(mixmonitor_ds->audiohook);
1083                 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
1084                 ast_audiohook_unlock(mixmonitor_ds->audiohook);
1085                 mixmonitor_ds->audiohook = NULL;
1086         }
1087
1088         ast_mutex_unlock(&mixmonitor_ds->lock);
1089
1090         /* Remove the datastore so the monitor thread can exit */
1091         if (!ast_channel_datastore_remove(chan, datastore)) {
1092                 ast_datastore_free(datastore);
1093         }
1094         ast_channel_unlock(chan);
1095
1096         return 0;
1097 }
1098
1099 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1100 {
1101         stop_mixmonitor_full(chan, data);
1102         return 0;
1103 }
1104
1105 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1106 {
1107         struct ast_channel *chan;
1108         struct ast_datastore *datastore = NULL;
1109         struct mixmonitor_ds *mixmonitor_ds = NULL;
1110
1111         switch (cmd) {
1112         case CLI_INIT:
1113                 e->command = "mixmonitor {start|stop|list}";
1114                 e->usage =
1115                         "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
1116                         "       The optional arguments are passed to the MixMonitor\n"
1117                         "       application when the 'start' command is used.\n";
1118                 return NULL;
1119         case CLI_GENERATE:
1120                 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1121         }
1122
1123         if (a->argc < 3) {
1124                 return CLI_SHOWUSAGE;
1125         }
1126
1127         if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1128                 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1129                 /* Technically this is a failure, but we don't want 2 errors printing out */
1130                 return CLI_SUCCESS;
1131         }
1132
1133         ast_channel_lock(chan);
1134
1135         if (!strcasecmp(a->argv[1], "start")) {
1136                 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1137                 ast_channel_unlock(chan);
1138         } else if (!strcasecmp(a->argv[1], "stop")){
1139                 ast_channel_unlock(chan);
1140                 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1141         } else if (!strcasecmp(a->argv[1], "list")) {
1142                 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1143                 ast_cli(a->fd, "=========================================================================\n");
1144                 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1145                         if (datastore->info == &mixmonitor_ds_info) {
1146                                 char *filename = "";
1147                                 char *filename_read = "";
1148                                 char *filename_write = "";
1149                                 mixmonitor_ds = datastore->data;
1150                                 if (mixmonitor_ds->fs)
1151                                         filename = ast_strdupa(mixmonitor_ds->fs->filename);
1152                                 if (mixmonitor_ds->fs_read)
1153                                         filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
1154                                 if (mixmonitor_ds->fs_write)
1155                                         filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
1156                                 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1157                         }
1158                 }
1159                 ast_channel_unlock(chan);
1160         } else {
1161                 ast_channel_unlock(chan);
1162                 chan = ast_channel_unref(chan);
1163                 return CLI_SHOWUSAGE;
1164         }
1165
1166         chan = ast_channel_unref(chan);
1167
1168         return CLI_SUCCESS;
1169 }
1170
1171 /*! \brief  Mute / unmute  a MixMonitor channel */
1172 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1173 {
1174         struct ast_channel *c = NULL;
1175
1176         const char *name = astman_get_header(m, "Channel");
1177         const char *id = astman_get_header(m, "ActionID");
1178         const char *state = astman_get_header(m, "State");
1179         const char *direction = astman_get_header(m,"Direction");
1180
1181         int clearmute = 1;
1182
1183         enum ast_audiohook_flags flag;
1184
1185         if (ast_strlen_zero(direction)) {
1186                 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1187                 return AMI_SUCCESS;
1188         }
1189
1190         if (!strcasecmp(direction, "read")) {
1191                 flag = AST_AUDIOHOOK_MUTE_READ;
1192         } else  if (!strcasecmp(direction, "write")) {
1193                 flag = AST_AUDIOHOOK_MUTE_WRITE;
1194         } else  if (!strcasecmp(direction, "both")) {
1195                 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
1196         } else {
1197                 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1198                 return AMI_SUCCESS;
1199         }
1200
1201         if (ast_strlen_zero(name)) {
1202                 astman_send_error(s, m, "No channel specified");
1203                 return AMI_SUCCESS;
1204         }
1205
1206         if (ast_strlen_zero(state)) {
1207                 astman_send_error(s, m, "No state specified");
1208                 return AMI_SUCCESS;
1209         }
1210
1211         clearmute = ast_false(state);
1212         c = ast_channel_get_by_name(name);
1213
1214         if (!c) {
1215                 astman_send_error(s, m, "No such channel");
1216                 return AMI_SUCCESS;
1217         }
1218
1219         if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
1220                 c = ast_channel_unref(c);
1221                 astman_send_error(s, m, "Cannot set mute flag");
1222                 return AMI_SUCCESS;
1223         }
1224
1225         astman_append(s, "Response: Success\r\n");
1226
1227         if (!ast_strlen_zero(id)) {
1228                 astman_append(s, "ActionID: %s\r\n", id);
1229         }
1230
1231         astman_append(s, "\r\n");
1232
1233         c = ast_channel_unref(c);
1234
1235         return AMI_SUCCESS;
1236 }
1237
1238 static int manager_mixmonitor(struct mansession *s, const struct message *m)
1239 {
1240         struct ast_channel *c = NULL;
1241
1242         const char *name = astman_get_header(m, "Channel");
1243         const char *id = astman_get_header(m, "ActionID");
1244         const char *file = astman_get_header(m, "File");
1245         const char *options = astman_get_header(m, "Options");
1246         char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1247         struct ast_flags flags = { 0 };
1248         char *uid_channel_var = NULL;
1249         const char *mixmonitor_id = NULL;
1250
1251         int res;
1252         char args[PATH_MAX] = "";
1253         if (ast_strlen_zero(name)) {
1254                 astman_send_error(s, m, "No channel specified");
1255                 return AMI_SUCCESS;
1256         }
1257
1258         c = ast_channel_get_by_name(name);
1259
1260         if (!c) {
1261                 astman_send_error(s, m, "No such channel");
1262                 return AMI_SUCCESS;
1263         }
1264
1265         if (!ast_strlen_zero(options)) {
1266                 ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
1267         }
1268
1269         snprintf(args, sizeof(args), "%s,%s", file, options);
1270
1271         ast_channel_lock(c);
1272         res = mixmonitor_exec(c, args);
1273
1274         if (ast_test_flag(&flags, MUXFLAG_UID)) {
1275                 uid_channel_var = opts[OPT_ARG_UID];
1276                 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1277         }
1278         ast_channel_unlock(c);
1279
1280         if (res) {
1281                 c = ast_channel_unref(c);
1282                 astman_send_error(s, m, "Could not start monitoring channel");
1283                 return AMI_SUCCESS;
1284         }
1285
1286         astman_append(s, "Response: Success\r\n");
1287
1288         if (!ast_strlen_zero(id)) {
1289                 astman_append(s, "ActionID: %s\r\n", id);
1290         }
1291
1292         if (!ast_strlen_zero(mixmonitor_id)) {
1293                 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1294         }
1295
1296         astman_append(s, "\r\n");
1297
1298         c = ast_channel_unref(c);
1299
1300         return AMI_SUCCESS;
1301 }
1302
1303 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1304 {
1305         struct ast_channel *c = NULL;
1306
1307         const char *name = astman_get_header(m, "Channel");
1308         const char *id = astman_get_header(m, "ActionID");
1309         const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1310
1311         int res;
1312         if (ast_strlen_zero(name)) {
1313                 astman_send_error(s, m, "No channel specified");
1314                 return AMI_SUCCESS;
1315         }
1316
1317         c = ast_channel_get_by_name(name);
1318
1319         if (!c) {
1320                 astman_send_error(s, m, "No such channel");
1321                 return AMI_SUCCESS;
1322         }
1323
1324         res = stop_mixmonitor_full(c, mixmonitor_id);
1325
1326         if (res) {
1327                 astman_send_error(s, m, "Could not stop monitoring channel");
1328                 return AMI_SUCCESS;
1329         }
1330
1331         astman_append(s, "Response: Success\r\n");
1332
1333         if (!ast_strlen_zero(id)) {
1334                 astman_append(s, "ActionID: %s\r\n", id);
1335         }
1336
1337         astman_append(s, "\r\n");
1338
1339         c = ast_channel_unref(c);
1340
1341         return AMI_SUCCESS;
1342 }
1343
1344 static struct ast_cli_entry cli_mixmonitor[] = {
1345         AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1346 };
1347
1348 static int unload_module(void)
1349 {
1350         int res;
1351
1352         ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1353         res = ast_unregister_application(stop_app);
1354         res |= ast_unregister_application(app);
1355         res |= ast_manager_unregister("MixMonitorMute");
1356         res |= ast_manager_unregister("MixMonitor");
1357         res |= ast_manager_unregister("StopMixMonitor");
1358
1359         return res;
1360 }
1361
1362 static int load_module(void)
1363 {
1364         int res;
1365
1366         ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1367         res = ast_register_application_xml(app, mixmonitor_exec);
1368         res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
1369         res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
1370         res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
1371         res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
1372
1373         return res;
1374 }
1375
1376 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");