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