Mix Monitor: Now with r and t options.
[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 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include "asterisk/paths.h"     /* use ast_config_AST_MONITOR_DIR */
41 #include "asterisk/file.h"
42 #include "asterisk/audiohook.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/module.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/app.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/autochan.h"
49 #include "asterisk/manager.h"
50
51 /*** DOCUMENTATION
52         <application name="MixMonitor" language="en_US">
53                 <synopsis>
54                         Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
55                         to guarantee the audio file is available for processing during dialplan execution.
56                 </synopsis>
57                 <syntax>
58                         <parameter name="file" required="true" argsep=".">
59                                 <argument name="filename" required="true">
60                                         <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
61                                         creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
62                                 </argument>
63                                 <argument name="extension" required="true" />
64                         </parameter>
65                         <parameter name="options">
66                                 <optionlist>
67                                         <option name="a">
68                                                 <para>Append to the file instead of overwriting it.</para>
69                                         </option>
70                                         <option name="b">
71                                                 <para>Only save audio to the file while the channel is bridged.</para>
72                                                 <note><para>Does not include conferences or sounds played to each bridged party</para></note>
73                                                 <note><para>If you utilize this option inside a Local channel, you must make sure the Local
74                                                 channel is not optimized away. To do this, be sure to call your Local channel with the
75                                                 <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
76                                         </option>
77                                         <option name="v">
78                                                 <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
79                                                 (range <literal>-4</literal> to <literal>4</literal>)</para>
80                                                 <argument name="x" required="true" />
81                                         </option>
82                                         <option name="V">
83                                                 <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
84                                                 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
85                                                 <argument name="x" required="true" />
86                                         </option>
87                                         <option name="W">
88                                                 <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
89                                                 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
90                                                 <argument name="x" required="true" />
91                                         </option>
92                                         <option name="r">
93                                                 <argument name="file" required="true" />
94                                                 <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
95                                                 Like with the basic filename argument, if an absolute path isn't given, it will create
96                                                 the file in the configured monitoring directory.</para>
97
98                                         </option>
99                                         <option name="t">
100                                                 <argument name="file" required="true" />
101                                                 <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
102                                                 Like with the basic filename argument, if an absolute path isn't given, it will create
103                                                 the file in the configured monitoring directory.</para>
104                                         </option>
105                                 </optionlist>
106                         </parameter>
107                         <parameter name="command">
108                                 <para>Will be executed when the recording is over.</para>
109                                 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
110                                 <para>All variables will be evaluated at the time MixMonitor is called.</para>
111                         </parameter>
112                 </syntax>
113                 <description>
114                         <para>Records the audio on the current channel to the specified file.</para>
115                         <variablelist>
116                                 <variable name="MIXMONITOR_FILENAME">
117                                         <para>Will contain the filename used to record.</para>
118                                 </variable>
119                         </variablelist> 
120                 </description>
121                 <see-also>
122                         <ref type="application">Monitor</ref>
123                         <ref type="application">StopMixMonitor</ref>
124                         <ref type="application">PauseMonitor</ref>
125                         <ref type="application">UnpauseMonitor</ref>
126                 </see-also>
127         </application>
128         <application name="StopMixMonitor" language="en_US">
129                 <synopsis>
130                         Stop recording a call through MixMonitor, and free the recording's file handle.
131                 </synopsis>
132                 <syntax />
133                 <description>
134                         <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
135                         on the current channel.</para>
136                 </description>
137                 <see-also>
138                         <ref type="application">MixMonitor</ref>
139                 </see-also>
140         </application>
141         <manager name="MixMonitorMute" language="en_US">
142                 <synopsis>
143                         Mute / unMute a Mixmonitor recording.
144                 </synopsis>
145                 <syntax>
146                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
147                         <parameter name="Channel" required="true">
148                                 <para>Used to specify the channel to mute.</para>
149                         </parameter>
150                         <parameter name="Direction">
151                                 <para>Which part of the recording to mute:  read, write or both (from channel, to channel or both channels).</para>
152                         </parameter>
153                         <parameter name="State">
154                                 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
155                         </parameter>
156                 </syntax>
157                 <description>
158                         <para>This action may be used to mute a MixMonitor recording.</para>
159                 </description>
160         </manager>
161
162  ***/
163
164 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
165
166 static const char * const app = "MixMonitor";
167
168 static const char * const stop_app = "StopMixMonitor";
169
170 static const char * const mixmonitor_spy_type = "MixMonitor";
171
172 struct mixmonitor {
173         struct ast_audiohook audiohook;
174         char *filename;
175         char *filename_read;
176         char *filename_write;
177         char *post_process;
178         char *name;
179         unsigned int flags;
180         struct ast_autochan *autochan;
181         struct mixmonitor_ds *mixmonitor_ds;
182 };
183
184 enum mixmonitor_flags {
185         MUXFLAG_APPEND = (1 << 1),
186         MUXFLAG_BRIDGED = (1 << 2),
187         MUXFLAG_VOLUME = (1 << 3),
188         MUXFLAG_READVOLUME = (1 << 4),
189         MUXFLAG_WRITEVOLUME = (1 << 5),
190         MUXFLAG_READ = (1 << 6),
191         MUXFLAG_WRITE = (1 << 7),
192         MUXFLAG_COMBINED = (1 << 8),
193 };
194
195 enum mixmonitor_args {
196         OPT_ARG_READVOLUME = 0,
197         OPT_ARG_WRITEVOLUME,
198         OPT_ARG_VOLUME,
199         OPT_ARG_WRITENAME,
200         OPT_ARG_READNAME,
201         OPT_ARG_ARRAY_SIZE,     /* Always last element of the enum */
202 };
203
204 AST_APP_OPTIONS(mixmonitor_opts, {
205         AST_APP_OPTION('a', MUXFLAG_APPEND),
206         AST_APP_OPTION('b', MUXFLAG_BRIDGED),
207         AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
208         AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
209         AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
210         AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
211         AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
212 });
213
214 struct mixmonitor_ds {
215         unsigned int destruction_ok;
216         ast_cond_t destruction_condition;
217         ast_mutex_t lock;
218
219         /* The filestream is held in the datastore so it can be stopped
220          * immediately during stop_mixmonitor or channel destruction. */
221         int fs_quit;
222
223         struct ast_filestream *fs;
224         struct ast_filestream *fs_read;
225         struct ast_filestream *fs_write;
226
227         struct ast_audiohook *audiohook;
228 };
229
230 /*!
231  * \internal
232  * \pre mixmonitor_ds must be locked before calling this function
233  */
234 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
235 {
236         unsigned char quitting = 0;
237
238         if (mixmonitor_ds->fs) {
239                 quitting = 1;
240                 ast_closestream(mixmonitor_ds->fs);
241                 mixmonitor_ds->fs = NULL;
242                 ast_verb(2, "MixMonitor close filestream (mixed)\n");
243         }
244
245         if (mixmonitor_ds->fs_read) {
246                 quitting = 1;
247                 ast_closestream(mixmonitor_ds->fs_read);
248                 mixmonitor_ds->fs_read = NULL;
249                 ast_verb(2, "MixMonitor close filestream (read)\n");
250         }
251
252         if (mixmonitor_ds->fs_write) {
253                 quitting = 1;
254                 ast_closestream(mixmonitor_ds->fs_write);
255                 mixmonitor_ds->fs_write = NULL;
256                 ast_verb(2, "MixMonitor close filestream (write)\n");
257         }
258
259         if (quitting) {
260                 mixmonitor_ds->fs_quit = 1;
261         }
262 }
263
264 static void mixmonitor_ds_destroy(void *data)
265 {
266         struct mixmonitor_ds *mixmonitor_ds = data;
267
268         ast_mutex_lock(&mixmonitor_ds->lock);
269         mixmonitor_ds->audiohook = NULL;
270         mixmonitor_ds->destruction_ok = 1;
271         ast_cond_signal(&mixmonitor_ds->destruction_condition);
272         ast_mutex_unlock(&mixmonitor_ds->lock);
273 }
274
275 static struct ast_datastore_info mixmonitor_ds_info = {
276         .type = "mixmonitor",
277         .destroy = mixmonitor_ds_destroy,
278 };
279
280 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
281 {
282         if (mixmonitor->mixmonitor_ds) {
283                 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
284                 mixmonitor->mixmonitor_ds->audiohook = NULL;
285                 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
286         }
287         /* kill the audiohook.*/
288         ast_audiohook_lock(&mixmonitor->audiohook);
289         ast_audiohook_detach(&mixmonitor->audiohook);
290         ast_audiohook_unlock(&mixmonitor->audiohook);
291         ast_audiohook_destroy(&mixmonitor->audiohook);
292 }
293
294 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) 
295 {
296         struct ast_channel *peer = NULL;
297         int res = 0;
298
299         if (!chan)
300                 return -1;
301
302         ast_audiohook_attach(chan, audiohook);
303
304         if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
305                 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
306
307         return res;
308 }
309
310 #define SAMPLES_PER_FRAME 160
311
312 static void mixmonitor_free(struct mixmonitor *mixmonitor)
313 {
314         if (mixmonitor) {
315                 if (mixmonitor->mixmonitor_ds) {
316                         ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
317                         ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
318                         ast_free(mixmonitor->filename_write);
319                         ast_free(mixmonitor->filename_read);
320                         ast_free(mixmonitor->mixmonitor_ds);
321                         ast_free(mixmonitor->name);
322                         ast_free(mixmonitor->post_process);
323                 }
324                 ast_free(mixmonitor);
325         }
326 }
327
328 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag)
329 {
330         /* Initialize the file if not already done so */
331         char *ext = NULL;
332         char *last_slash = NULL;
333         if (!ast_strlen_zero(filename)) {
334                 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
335                         *oflags = O_CREAT | O_WRONLY;
336                         *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
337                         
338                         last_slash = strrchr(filename, '/');
339
340                         if ((ext = strrchr(filename, '.')) && (ext > last_slash)) {
341                                 *(ext++) = '\0';
342                         } else {
343                                 ext = "raw";
344                         }
345
346                         if (!(*fs = ast_writefile(filename, ext, NULL, *oflags, 0, 0666))) {
347                                 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, ext);
348                                 *errflag = 1;
349                         }
350                 }
351         }
352 }
353
354 static void *mixmonitor_thread(void *obj) 
355 {
356         struct mixmonitor *mixmonitor = obj;
357
358         struct ast_filestream **fs = NULL;
359         struct ast_filestream **fs_read = NULL;
360         struct ast_filestream **fs_write = NULL;
361
362         unsigned int oflags;
363         int errflag = 0;
364         struct ast_format format_slin;
365
366         ast_format_set(&format_slin, AST_FORMAT_SLINEAR, 0);
367         ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
368
369         fs = &mixmonitor->mixmonitor_ds->fs;
370         fs_read = &mixmonitor->mixmonitor_ds->fs_read;
371         fs_write = &mixmonitor->mixmonitor_ds->fs_write;
372
373         /* The audiohook must enter and exit the loop locked */
374         ast_audiohook_lock(&mixmonitor->audiohook);
375         while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
376                 struct ast_frame *fr = NULL;
377                 struct ast_frame *fr_read = NULL;
378                 struct ast_frame *fr_write = NULL;
379
380                 if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
381                                                 &fr_read, &fr_write))) {
382                         ast_audiohook_trigger_wait(&mixmonitor->audiohook);
383
384                         if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
385                                 break;
386                         }
387                         continue;
388                 }
389
390                 /* audiohook lock is not required for the next block.
391                  * Unlock it, but remember to lock it before looping or exiting */
392                 ast_audiohook_unlock(&mixmonitor->audiohook);
393
394                 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
395                         ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
396                         mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag);
397                         mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag);
398                         mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag);
399
400                         /* Write out the frame(s) */
401                         if ((*fs_read) && (fr_read)) {
402                                 struct ast_frame *cur;
403
404                                 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
405                                         ast_writestream(*fs_read, cur);
406                                 }
407                         }
408
409                         if ((*fs_write) && (fr_write)) {
410                                 struct ast_frame *cur;
411
412                                 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
413                                         ast_writestream(*fs_write, cur);
414                                 }
415                         }
416
417                         if ((*fs) && (fr)) {
418                                 struct ast_frame *cur;
419
420                                 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
421                                         ast_writestream(*fs, cur);
422                                 }
423                         }
424                         ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
425                 }
426                 /* All done! free it. */
427                 if (fr) {
428                         ast_frame_free(fr, 0);
429                 }
430                 if (fr_read) {
431                         ast_frame_free(fr_read, 0);
432                 }
433                 if (fr_write) {
434                         ast_frame_free(fr_write, 0);
435                 }
436
437                 fr = NULL;
438                 fr_write = NULL;
439                 fr_read = NULL;
440
441                 ast_audiohook_lock(&mixmonitor->audiohook);
442         }
443         ast_audiohook_unlock(&mixmonitor->audiohook);
444
445         ast_autochan_destroy(mixmonitor->autochan);
446
447         /* Datastore cleanup.  close the filestream and wait for ds destruction */
448         ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
449         mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
450         if (!mixmonitor->mixmonitor_ds->destruction_ok) {
451                 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
452         }
453         ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
454
455         /* kill the audiohook */
456         destroy_monitor_audiohook(mixmonitor);
457
458         if (mixmonitor->post_process) {
459                 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
460                 ast_safe_system(mixmonitor->post_process);
461         }
462
463         ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
464         mixmonitor_free(mixmonitor);
465         return NULL;
466 }
467
468 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
469 {
470         struct ast_datastore *datastore = NULL;
471         struct mixmonitor_ds *mixmonitor_ds;
472
473         if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
474                 return -1;
475         }
476
477         ast_mutex_init(&mixmonitor_ds->lock);
478         ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
479
480         if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
481                 ast_mutex_destroy(&mixmonitor_ds->lock);
482                 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
483                 ast_free(mixmonitor_ds);
484                 return -1;
485         }
486
487         mixmonitor_ds->audiohook = &mixmonitor->audiohook;
488         datastore->data = mixmonitor_ds;
489
490         ast_channel_lock(chan);
491         ast_channel_datastore_add(chan, datastore);
492         ast_channel_unlock(chan);
493
494         mixmonitor->mixmonitor_ds = mixmonitor_ds;
495         return 0;
496 }
497
498 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
499                                   unsigned int flags, int readvol, int writevol,
500                                   const char *post_process, const char *filename_write,
501                                   const char *filename_read) 
502 {
503         pthread_t thread;
504         struct mixmonitor *mixmonitor;
505         char postprocess2[1024] = "";
506
507         postprocess2[0] = 0;
508         /* If a post process system command is given attach it to the structure */
509         if (!ast_strlen_zero(post_process)) {
510                 char *p1, *p2;
511
512                 p1 = ast_strdupa(post_process);
513                 for (p2 = p1; *p2; p2++) {
514                         if (*p2 == '^' && *(p2+1) == '{') {
515                                 *p2 = '$';
516                         }
517                 }
518                 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
519         }
520
521         /* Pre-allocate mixmonitor structure and spy */
522         if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
523                 return;
524         }
525
526         /* Setup the actual spy before creating our thread */
527         if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
528                 mixmonitor_free(mixmonitor);
529                 return;
530         }
531
532         /* Copy over flags and channel name */
533         mixmonitor->flags = flags;
534         if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
535                 mixmonitor_free(mixmonitor);
536                 return;
537         }
538
539         if (setup_mixmonitor_ds(mixmonitor, chan)) {
540                 ast_autochan_destroy(mixmonitor->autochan);
541                 mixmonitor_free(mixmonitor);
542                 return;
543         }
544
545         mixmonitor->name = ast_strdup(chan->name);
546
547         if (!ast_strlen_zero(postprocess2)) {
548                 mixmonitor->post_process = ast_strdup(postprocess2);
549         }
550
551         if (!ast_strlen_zero(filename)) {
552                 mixmonitor->filename = ast_strdup(filename);
553         }
554
555         if (!ast_strlen_zero(filename_write)) {
556                 mixmonitor->filename_write = ast_strdup(filename_write);
557         }
558
559         if (!ast_strlen_zero(filename_read)) {
560                 mixmonitor->filename_read = ast_strdup(filename_read);
561         }
562
563         ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
564
565         if (readvol)
566                 mixmonitor->audiohook.options.read_volume = readvol;
567         if (writevol)
568                 mixmonitor->audiohook.options.write_volume = writevol;
569
570         if (startmon(chan, &mixmonitor->audiohook)) {
571                 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
572                         mixmonitor_spy_type, chan->name);
573                 ast_audiohook_destroy(&mixmonitor->audiohook);
574                 mixmonitor_free(mixmonitor);
575                 return;
576         }
577
578         ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
579 }
580
581 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
582 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
583 static char *filename_parse(char *filename, char *buffer, size_t len)
584 {
585         char *slash;
586         if (ast_strlen_zero(filename)) {
587                 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
588         } else if (filename[0] != '/') {
589                 char *build;
590                 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
591                 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
592                 filename = build;
593         }
594
595         ast_copy_string(buffer, filename, len);
596
597         if ((slash = strrchr(filename, '/'))) {
598                 *slash = '\0';
599         }
600         ast_mkdir(filename, 0777);
601
602         return buffer;
603 }
604
605 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
606 {
607         int x, readvol = 0, writevol = 0;
608         char *filename_read = NULL;
609         char *filename_write = NULL;
610         char filename_buffer[1024] = "";
611
612         struct ast_flags flags = { 0 };
613         char *parse;
614         AST_DECLARE_APP_ARGS(args,
615                 AST_APP_ARG(filename);
616                 AST_APP_ARG(options);
617                 AST_APP_ARG(post_process);
618         );
619         
620         if (ast_strlen_zero(data)) {
621                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
622                 return -1;
623         }
624
625         parse = ast_strdupa(data);
626
627         AST_STANDARD_APP_ARGS(args, parse);
628
629         if (args.options) {
630                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
631
632                 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
633
634                 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
635                         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
636                                 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
637                         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
638                                 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
639                         } else {
640                                 readvol = get_volfactor(x);
641                         }
642                 }
643                 
644                 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
645                         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
646                                 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
647                         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
648                                 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
649                         } else {
650                                 writevol = get_volfactor(x);
651                         }
652                 }
653                 
654                 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
655                         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
656                                 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
657                         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
658                                 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
659                         } else {
660                                 readvol = writevol = get_volfactor(x);
661                         }
662                 }
663
664                 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
665                         filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
666                 }
667
668                 if (ast_test_flag(&flags, MUXFLAG_READ)) {
669                         filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
670                 }
671         }
672
673         /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
674
675         if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
676                 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
677                 return -1;
678         }
679
680         /* If filename exists, try to create directories for it */
681         if (!(ast_strlen_zero(args.filename))) {
682                 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
683         }
684
685         pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
686         launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read);
687
688         return 0;
689 }
690
691 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
692 {
693         struct ast_datastore *datastore = NULL;
694
695         ast_channel_lock(chan);
696         ast_audiohook_detach_source(chan, mixmonitor_spy_type);
697         if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
698                 struct mixmonitor_ds *mixmonitor_ds = datastore->data;
699
700                 ast_mutex_lock(&mixmonitor_ds->lock);
701
702                 /* closing the filestream here guarantees the file is avaliable to the dialplan
703                  * after calling StopMixMonitor */
704                 mixmonitor_ds_close_fs(mixmonitor_ds);
705
706                 /* The mixmonitor thread may be waiting on the audiohook trigger.
707                  * In order to exit from the mixmonitor loop before waiting on channel
708                  * destruction, poke the audiohook trigger. */
709                 if (mixmonitor_ds->audiohook) {
710                         ast_audiohook_lock(mixmonitor_ds->audiohook);
711                         ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
712                         ast_audiohook_unlock(mixmonitor_ds->audiohook);
713                         mixmonitor_ds->audiohook = NULL;
714                 }
715
716                 ast_mutex_unlock(&mixmonitor_ds->lock);
717
718                 /* Remove the datastore so the monitor thread can exit */
719                 if (!ast_channel_datastore_remove(chan, datastore)) {
720                         ast_datastore_free(datastore);
721                 }
722         }
723         ast_channel_unlock(chan);
724
725         return 0;
726 }
727
728 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
729 {
730         struct ast_channel *chan;
731
732         switch (cmd) {
733         case CLI_INIT:
734                 e->command = "mixmonitor {start|stop}";
735                 e->usage =
736                         "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
737                         "       The optional arguments are passed to the MixMonitor\n"
738                         "       application when the 'start' command is used.\n";
739                 return NULL;
740         case CLI_GENERATE:
741                 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
742         }
743
744         if (a->argc < 3)
745                 return CLI_SHOWUSAGE;
746
747         if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
748                 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
749                 /* Technically this is a failure, but we don't want 2 errors printing out */
750                 return CLI_SUCCESS;
751         }
752
753         ast_channel_lock(chan);
754
755         if (!strcasecmp(a->argv[1], "start")) {
756                 mixmonitor_exec(chan, a->argv[3]);
757                 ast_channel_unlock(chan);
758         } else {
759                 ast_channel_unlock(chan);
760                 ast_audiohook_detach_source(chan, mixmonitor_spy_type);
761         }
762
763         chan = ast_channel_unref(chan);
764
765         return CLI_SUCCESS;
766 }
767
768 /*! \brief  Mute / unmute  a MixMonitor channel */
769 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
770 {
771         struct ast_channel *c = NULL;
772
773         const char *name = astman_get_header(m, "Channel");
774         const char *id = astman_get_header(m, "ActionID");
775         const char *state = astman_get_header(m, "State");
776         const char *direction = astman_get_header(m,"Direction");
777
778         int clearmute = 1;
779
780         enum ast_audiohook_flags flag;
781
782         if (ast_strlen_zero(direction)) {
783                 astman_send_error(s, m, "No direction specified. Must be read, write or both");
784                 return AMI_SUCCESS;
785         }
786
787         if (!strcasecmp(direction, "read")) {
788                 flag = AST_AUDIOHOOK_MUTE_READ;
789         } else  if (!strcasecmp(direction, "write")) {
790                 flag = AST_AUDIOHOOK_MUTE_WRITE;
791         } else  if (!strcasecmp(direction, "both")) {
792                 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
793         } else {
794                 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
795                 return AMI_SUCCESS;
796         }
797
798         if (ast_strlen_zero(name)) {
799                 astman_send_error(s, m, "No channel specified");
800                 return AMI_SUCCESS;
801         }
802
803         if (ast_strlen_zero(state)) {
804                 astman_send_error(s, m, "No state specified");
805                 return AMI_SUCCESS;
806         }
807
808         clearmute = ast_false(state);
809         c = ast_channel_get_by_name(name);
810
811         if (!c) {
812                 astman_send_error(s, m, "No such channel");
813                 return AMI_SUCCESS;
814         }
815
816         if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
817                 c = ast_channel_unref(c);
818                 astman_send_error(s, m, "Cannot set mute flag");
819                 return AMI_SUCCESS;
820         }
821
822         astman_append(s, "Response: Success\r\n");
823
824         if (!ast_strlen_zero(id)) {
825                 astman_append(s, "ActionID: %s\r\n", id);
826         }
827
828         astman_append(s, "\r\n");
829
830         c = ast_channel_unref(c);
831
832         return AMI_SUCCESS;
833 }
834
835 static struct ast_cli_entry cli_mixmonitor[] = {
836         AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
837 };
838
839 static int unload_module(void)
840 {
841         int res;
842
843         ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
844         res = ast_unregister_application(stop_app);
845         res |= ast_unregister_application(app);
846         res |= ast_manager_unregister("MixMonitorMute");
847         
848         return res;
849 }
850
851 static int load_module(void)
852 {
853         int res;
854
855         ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
856         res = ast_register_application_xml(app, mixmonitor_exec);
857         res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
858         res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
859
860         return res;
861 }
862
863 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");