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