Asterisk - The Open Source Telephony Project  21.4.1
app_mixmonitor.c
Go to the documentation of this file.
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  <use type="module">func_periodic_hook</use>
38  <support_level>core</support_level>
39  ***/
40 
41 #include "asterisk.h"
42 
43 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
44 #include "asterisk/stringfields.h"
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/stasis.h"
55 #include "asterisk/stasis_channels.h"
56 #include "asterisk/callerid.h"
57 #include "asterisk/mod_format.h"
58 #include "asterisk/linkedlists.h"
59 #include "asterisk/test.h"
60 #include "asterisk/mixmonitor.h"
61 #include "asterisk/format_cache.h"
62 #include "asterisk/beep.h"
63 
64 /*** DOCUMENTATION
65  <application name="MixMonitor" language="en_US">
66  <synopsis>
67  Record a call and mix the audio during the recording. Use of StopMixMonitor is required
68  to guarantee the audio file is available for processing during dialplan execution.
69  </synopsis>
70  <syntax>
71  <parameter name="file" required="true" argsep=".">
72  <argument name="filename" required="true">
73  <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
74  creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
75  </argument>
76  <argument name="extension" required="true" />
77  </parameter>
78  <parameter name="options">
79  <optionlist>
80  <option name="a">
81  <para>Append to the file instead of overwriting it.</para>
82  </option>
83  <option name="b">
84  <para>Only save audio to the file while the channel is bridged.</para>
85  <note><para>If you utilize this option inside a Local channel, you must make sure the Local
86  channel is not optimized away. To do this, be sure to call your Local channel with the
87  <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
88  </option>
89  <option name="B">
90  <para>Play a periodic beep while this call is being recorded.</para>
91  <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
92  </option>
93  <option name="c">
94  <para>Use the real Caller ID from the channel for the voicemail Caller ID.</para>
95  <para>By default, the Connected Line is used. If you want the channel caller's
96  real number, you may need to specify this option.</para>
97  </option>
98  <option name="d">
99  <para>Delete the recording file as soon as MixMonitor is done with it.</para>
100  <para>For example, if you use the m option to dispatch the recording to a voicemail box,
101  you can specify this option to delete the original copy of it afterwards.</para>
102  </option>
103  <option name="v">
104  <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
105  (range <literal>-4</literal> to <literal>4</literal>)</para>
106  <argument name="x" required="true" />
107  </option>
108  <option name="V">
109  <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
110  of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
111  <argument name="x" required="true" />
112  </option>
113  <option name="W">
114  <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
115  of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
116  <argument name="x" required="true" />
117  </option>
118  <option name="r">
119  <argument name="file" required="true" />
120  <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
121  Like with the basic filename argument, if an absolute path isn't given, it will create
122  the file in the configured monitoring directory.</para>
123  </option>
124  <option name="t">
125  <argument name="file" required="true" />
126  <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
127  Like with the basic filename argument, if an absolute path isn't given, it will create
128  the file in the configured monitoring directory.</para>
129  </option>
130  <option name="n">
131  <para>When the <replaceable>r</replaceable> or <replaceable>t</replaceable> option is
132  used, MixMonitor will insert silence into the specified files to maintain
133  synchronization between them. Use this option to disable that behavior.</para>
134  </option>
135  <option name="i">
136  <argument name="chanvar" required="true" />
137  <para>Stores the MixMonitor's ID on this channel variable.</para>
138  </option>
139  <option name="p">
140  <para>Play a beep on the channel that starts the recording.</para>
141  </option>
142  <option name="P">
143  <para>Play a beep on the channel that stops the recording.</para>
144  </option>
145  <option name="m">
146  <argument name="mailbox" required="true" />
147  <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
148  separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
149  the syntax: mailbox@context/folder</para>
150  </option>
151  </optionlist>
152  </parameter>
153  <parameter name="command">
154  <para>Will be executed when the recording is over.</para>
155  <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
156  <para>All variables will be evaluated at the time MixMonitor is called.</para>
157  <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
158  or <variable>CALLERID(name)</variable> as part of the command parameters. You
159  risk a command injection attack executing arbitrary commands if the untrusted
160  strings aren't filtered to remove dangerous characters. See function
161  <variable>FILTER()</variable>.</para></warning>
162  </parameter>
163  </syntax>
164  <description>
165  <para>Records the audio on the current channel to the specified file.</para>
166  <para>This application does not automatically answer and should be preceeded by
167  an application such as Answer or Progress().</para>
168  <note><para>MixMonitor runs as an audiohook.</para></note>
169  <note><para>If a filename passed to MixMonitor ends with
170  <literal>.wav49</literal>, Asterisk will silently convert the extension to
171  <literal>.WAV</literal> for legacy reasons. <variable>MIXMONITOR_FILENAME</variable>
172  will contain the actual filename that Asterisk is writing to, not necessarily the
173  value that was passed in.</para></note>
174  <variablelist>
175  <variable name="MIXMONITOR_FILENAME">
176  <para>Will contain the filename used to record.</para>
177  </variable>
178  </variablelist>
179  <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
180  or <variable>CALLERID(name)</variable> as part of ANY of the application's
181  parameters. You risk a command injection attack executing arbitrary commands
182  if the untrusted strings aren't filtered to remove dangerous characters. See
183  function <variable>FILTER()</variable>.</para></warning>
184  </description>
185  <see-also>
186  <ref type="application">StopMixMonitor</ref>
187  </see-also>
188  </application>
189  <application name="StopMixMonitor" language="en_US">
190  <synopsis>
191  Stop recording a call through MixMonitor, and free the recording's file handle.
192  </synopsis>
193  <syntax>
194  <parameter name="MixMonitorID" required="false">
195  <para>If a valid ID is provided, then this command will stop only that specific
196  MixMonitor.</para>
197  </parameter>
198  </syntax>
199  <description>
200  <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
201  on the current channel.</para>
202  </description>
203  <see-also>
204  <ref type="application">MixMonitor</ref>
205  </see-also>
206  </application>
207  <manager name="MixMonitorMute" language="en_US">
208  <synopsis>
209  Mute / unMute a Mixmonitor recording.
210  </synopsis>
211  <syntax>
212  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
213  <parameter name="Channel" required="true">
214  <para>Used to specify the channel to mute.</para>
215  </parameter>
216  <parameter name="Direction">
217  <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
218  </parameter>
219  <parameter name="State">
220  <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
221  </parameter>
222  </syntax>
223  <description>
224  <para>This action may be used to mute a MixMonitor recording.</para>
225  </description>
226  </manager>
227  <manager name="MixMonitor" language="en_US">
228  <synopsis>
229  Record a call and mix the audio during the recording. Use of StopMixMonitor is required
230  to guarantee the audio file is available for processing during dialplan execution.
231  </synopsis>
232  <syntax>
233  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
234  <parameter name="Channel" required="true">
235  <para>Used to specify the channel to record.</para>
236  </parameter>
237  <parameter name="File">
238  <para>Is the name of the file created in the monitor spool directory.
239  Defaults to the same name as the channel (with slashes replaced with dashes).
240  This argument is optional if you specify to record unidirectional audio with
241  either the r(filename) or t(filename) options in the options field. If
242  neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
243  be recorded.</para>
244  </parameter>
245  <parameter name="options">
246  <para>Options that apply to the MixMonitor in the same way as they
247  would apply if invoked from the MixMonitor application. For a list of
248  available options, see the documentation for the mixmonitor application. </para>
249  </parameter>
250  <parameter name="Command">
251  <para>Will be executed when the recording is over.
252  Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
253  All variables will be evaluated at the time MixMonitor is called.</para>
254  <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
255  or <variable>CALLERID(name)</variable> as part of the command parameters. You
256  risk a command injection attack executing arbitrary commands if the untrusted
257  strings aren't filtered to remove dangerous characters. See function
258  <variable>FILTER()</variable>.</para></warning>
259  </parameter>
260  </syntax>
261  <description>
262  <para>This action records the audio on the current channel to the specified file.</para>
263  <variablelist>
264  <variable name="MIXMONITOR_FILENAME">
265  <para>Will contain the filename used to record the mixed stream.</para>
266  </variable>
267  </variablelist>
268  </description>
269  </manager>
270  <manager name="StopMixMonitor" language="en_US">
271  <synopsis>
272  Stop recording a call through MixMonitor, and free the recording's file handle.
273  </synopsis>
274  <syntax>
275  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
276  <parameter name="Channel" required="true">
277  <para>The name of the channel monitored.</para>
278  </parameter>
279  <parameter name="MixMonitorID" required="false">
280  <para>If a valid ID is provided, then this command will stop only that specific
281  MixMonitor.</para>
282  </parameter>
283  </syntax>
284  <description>
285  <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
286  action on the current channel.</para>
287  </description>
288  </manager>
289  <function name="MIXMONITOR" language="en_US">
290  <synopsis>
291  Retrieve data pertaining to specific instances of MixMonitor on a channel.
292  </synopsis>
293  <syntax>
294  <parameter name="id" required="true">
295  <para>The unique ID of the MixMonitor instance. The unique ID can be retrieved through the channel
296  variable used as an argument to the <replaceable>i</replaceable> option to MixMonitor.</para>
297  </parameter>
298  <parameter name="key" required="true">
299  <para>The piece of data to retrieve from the MixMonitor.</para>
300  <enumlist>
301  <enum name="filename" />
302  </enumlist>
303  </parameter>
304  </syntax>
305  </function>
306  <managerEvent language="en_US" name="MixMonitorStart">
307  <managerEventInstance class="EVENT_FLAG_CALL">
308  <synopsis>Raised when monitoring has started on a channel.</synopsis>
309  <syntax>
310  <channel_snapshot/>
311  </syntax>
312  <see-also>
313  <ref type="managerEvent">MixMonitorStop</ref>
314  <ref type="application">MixMonitor</ref>
315  <ref type="manager">MixMonitor</ref>
316  </see-also>
317  </managerEventInstance>
318  </managerEvent>
319  <managerEvent language="en_US" name="MixMonitorStop">
320  <managerEventInstance class="EVENT_FLAG_CALL">
321  <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
322  <syntax>
323  <channel_snapshot/>
324  </syntax>
325  <see-also>
326  <ref type="managerEvent">MixMonitorStart</ref>
327  <ref type="application">StopMixMonitor</ref>
328  <ref type="manager">StopMixMonitor</ref>
329  </see-also>
330  </managerEventInstance>
331  </managerEvent>
332  <managerEvent language="en_US" name="MixMonitorMute">
333  <managerEventInstance class="EVENT_FLAG_CALL">
334  <synopsis>Raised when monitoring is muted or unmuted on a channel.</synopsis>
335  <syntax>
336  <channel_snapshot/>
337  <parameter name="Direction">
338  <para>Which part of the recording was muted or unmuted: read, write or both
339  (from channel, to channel or both directions).</para>
340  </parameter>
341  <parameter name="State">
342  <para>If the monitoring was muted or unmuted: 1 when muted, 0 when unmuted.</para>
343  </parameter>
344  </syntax>
345  <see-also>
346  <ref type="manager">MixMonitorMute</ref>
347  </see-also>
348  </managerEventInstance>
349  </managerEvent>
350 
351 
352  ***/
353 
354 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
355 
356 static const char * const app = "MixMonitor";
357 
358 static const char * const stop_app = "StopMixMonitor";
359 
360 static const char * const mixmonitor_spy_type = "MixMonitor";
361 
362 /*!
363  * \internal
364  * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
365  */
366 struct vm_recipient {
367  char mailbox[AST_MAX_CONTEXT];
368  char context[AST_MAX_EXTENSION];
369  char folder[80];
371 };
372 
373 struct mixmonitor {
374  struct ast_audiohook audiohook;
375  char *filename;
376  char *filename_read;
377  char *filename_write;
378  char *post_process;
379  char *name;
380  ast_callid callid;
381  unsigned int flags;
382  struct ast_autochan *autochan;
384 
385  /* the below string fields describe data used for creating voicemails from the recording */
387  AST_STRING_FIELD(call_context);
388  AST_STRING_FIELD(call_extension);
389  AST_STRING_FIELD(call_callerchan);
390  AST_STRING_FIELD(call_callerid);
391  );
392  int call_priority;
393 
394  /* FUTURE DEVELOPMENT NOTICE
395  * recipient_list will need locks if we make it editable after the monitor is started */
396  AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
397 };
398 
399 enum mixmonitor_flags {
400  MUXFLAG_APPEND = (1 << 1),
401  MUXFLAG_BRIDGED = (1 << 2),
402  MUXFLAG_VOLUME = (1 << 3),
403  MUXFLAG_READVOLUME = (1 << 4),
404  MUXFLAG_WRITEVOLUME = (1 << 5),
405  MUXFLAG_READ = (1 << 6),
406  MUXFLAG_WRITE = (1 << 7),
407  MUXFLAG_COMBINED = (1 << 8),
408  MUXFLAG_UID = (1 << 9),
409  MUXFLAG_VMRECIPIENTS = (1 << 10),
410  MUXFLAG_BEEP = (1 << 11),
411  MUXFLAG_BEEP_START = (1 << 12),
412  MUXFLAG_BEEP_STOP = (1 << 13),
413  MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
414  MUXFLAG_NO_RWSYNC = (1 << 15),
415  MUXFLAG_AUTO_DELETE = (1 << 16),
416  MUXFLAG_REAL_CALLERID = (1 << 17),
417 };
418 
419 enum mixmonitor_args {
420  OPT_ARG_READVOLUME = 0,
421  OPT_ARG_WRITEVOLUME,
422  OPT_ARG_VOLUME,
423  OPT_ARG_WRITENAME,
424  OPT_ARG_READNAME,
425  OPT_ARG_UID,
426  OPT_ARG_VMRECIPIENTS,
427  OPT_ARG_BEEP_INTERVAL,
428  OPT_ARG_DEPRECATED_RWSYNC,
429  OPT_ARG_NO_RWSYNC,
430  OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
431 };
432 
433 AST_APP_OPTIONS(mixmonitor_opts, {
434  AST_APP_OPTION('a', MUXFLAG_APPEND),
435  AST_APP_OPTION('b', MUXFLAG_BRIDGED),
436  AST_APP_OPTION_ARG('B', MUXFLAG_BEEP, OPT_ARG_BEEP_INTERVAL),
437  AST_APP_OPTION('c', MUXFLAG_REAL_CALLERID),
438  AST_APP_OPTION('d', MUXFLAG_AUTO_DELETE),
439  AST_APP_OPTION('p', MUXFLAG_BEEP_START),
440  AST_APP_OPTION('P', MUXFLAG_BEEP_STOP),
441  AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
442  AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
443  AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
444  AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
445  AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
446  AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
447  AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
448  AST_APP_OPTION_ARG('S', MUXFLAG_DEPRECATED_RWSYNC, OPT_ARG_DEPRECATED_RWSYNC),
449  AST_APP_OPTION_ARG('n', MUXFLAG_NO_RWSYNC, OPT_ARG_NO_RWSYNC),
450 });
451 
453  unsigned int destruction_ok;
454  ast_cond_t destruction_condition;
455  ast_mutex_t lock;
456 
457  /* The filestream is held in the datastore so it can be stopped
458  * immediately during stop_mixmonitor or channel destruction. */
459  int fs_quit;
460 
461  struct ast_filestream *fs;
462  struct ast_filestream *fs_read;
463  struct ast_filestream *fs_write;
464 
465  struct ast_audiohook *audiohook;
466 
467  unsigned int samp_rate;
468  char *filename;
469  char *beep_id;
470 };
471 
472 /*!
473  * \internal
474  * \pre mixmonitor_ds must be locked before calling this function
475  */
476 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
477 {
478  unsigned char quitting = 0;
479 
480  if (mixmonitor_ds->fs) {
481  quitting = 1;
482  ast_closestream(mixmonitor_ds->fs);
483  mixmonitor_ds->fs = NULL;
484  ast_verb(2, "MixMonitor close filestream (mixed)\n");
485  }
486 
487  if (mixmonitor_ds->fs_read) {
488  quitting = 1;
489  ast_closestream(mixmonitor_ds->fs_read);
490  mixmonitor_ds->fs_read = NULL;
491  ast_verb(2, "MixMonitor close filestream (read)\n");
492  }
493 
494  if (mixmonitor_ds->fs_write) {
495  quitting = 1;
496  ast_closestream(mixmonitor_ds->fs_write);
497  mixmonitor_ds->fs_write = NULL;
498  ast_verb(2, "MixMonitor close filestream (write)\n");
499  }
500 
501  if (quitting) {
502  mixmonitor_ds->fs_quit = 1;
503  }
504 }
505 
506 static void mixmonitor_ds_destroy(void *data)
507 {
508  struct mixmonitor_ds *mixmonitor_ds = data;
509 
510  ast_mutex_lock(&mixmonitor_ds->lock);
511  mixmonitor_ds->audiohook = NULL;
512  mixmonitor_ds->destruction_ok = 1;
513  ast_free(mixmonitor_ds->filename);
514  ast_free(mixmonitor_ds->beep_id);
515  ast_cond_signal(&mixmonitor_ds->destruction_condition);
516  ast_mutex_unlock(&mixmonitor_ds->lock);
517 }
518 
519 static const struct ast_datastore_info mixmonitor_ds_info = {
520  .type = "mixmonitor",
521  .destroy = mixmonitor_ds_destroy,
522 };
523 
524 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
525 {
526  if (mixmonitor->mixmonitor_ds) {
527  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
528  mixmonitor->mixmonitor_ds->audiohook = NULL;
529  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
530  }
531  /* kill the audiohook.*/
532  ast_audiohook_lock(&mixmonitor->audiohook);
533  ast_audiohook_detach(&mixmonitor->audiohook);
534  ast_audiohook_unlock(&mixmonitor->audiohook);
535  ast_audiohook_destroy(&mixmonitor->audiohook);
536 }
537 
538 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
539 {
540  if (!chan) {
541  return -1;
542  }
543 
544  return ast_audiohook_attach(chan, audiohook);
545 }
546 
547 /*!
548  * \internal
549  * \brief adds recipients to a mixmonitor's recipient list
550  * \param mixmonitor mixmonitor being affected
551  * \param vm_recipients string containing the desired recipients to add
552  */
553 static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
554 {
555  /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
556  char *cur_mailbox = ast_strdupa(vm_recipients);
557  char *cur_context;
558  char *cur_folder;
559  char *next;
560  int elements_processed = 0;
561 
562  while (!ast_strlen_zero(cur_mailbox)) {
563  ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
564  if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
565  *(next++) = '\0';
566  }
567 
568  if ((cur_folder = strchr(cur_mailbox, '/'))) {
569  *(cur_folder++) = '\0';
570  } else {
571  cur_folder = "INBOX";
572  }
573 
574  if ((cur_context = strchr(cur_mailbox, '@'))) {
575  *(cur_context++) = '\0';
576  } else {
577  cur_context = "default";
578  }
579 
580  if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
581  struct vm_recipient *recipient;
582  if (!(recipient = ast_malloc(sizeof(*recipient)))) {
583  ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
584  return;
585  }
586  ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
587  ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
588  ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
589 
590  /* Add to list */
591  ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
592  AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
593  } else {
594  ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
595  }
596 
597  cur_mailbox = next;
598  elements_processed++;
599  }
600 }
601 
602 static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
603 {
604  struct vm_recipient *current;
605  while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
606  /* Clear list element data */
607  ast_free(current);
608  }
609 }
610 
611 #define SAMPLES_PER_FRAME 160
612 
613 static void mixmonitor_free(struct mixmonitor *mixmonitor)
614 {
615  if (mixmonitor) {
616  if (mixmonitor->mixmonitor_ds) {
617  ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
618  ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
619  ast_free(mixmonitor->mixmonitor_ds);
620  }
621 
622  ast_free(mixmonitor->name);
623  ast_free(mixmonitor->post_process);
624  ast_free(mixmonitor->filename);
625  ast_free(mixmonitor->filename_write);
626  ast_free(mixmonitor->filename_read);
627 
628  /* Free everything in the recipient list */
629  clear_mixmonitor_recipient_list(mixmonitor);
630 
631  /* clean stringfields */
632  ast_string_field_free_memory(mixmonitor);
633 
634  ast_free(mixmonitor);
635  }
636 }
637 
638 /*!
639  * \internal
640  * \brief Copies the mixmonitor to all voicemail recipients
641  * \param mixmonitor The mixmonitor that needs to forward its file to recipients
642  * \param ext Format of the file that was saved
643  * \param filename
644  */
645 static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
646 {
647  struct vm_recipient *recipient = NULL;
648  struct ast_vm_recording_data recording_data;
649  if (ast_string_field_init(&recording_data, 512)) {
650  ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
651  return;
652  }
653 
654  /* Copy strings to stringfields that will be used for all recipients */
655  ast_string_field_set(&recording_data, recording_file, filename);
656  ast_string_field_set(&recording_data, recording_ext, ext);
657  ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
658  ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
659  ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
660  ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
661  /* and call_priority gets copied too */
662  recording_data.call_priority = mixmonitor->call_priority;
663 
664  AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
665  /* context, mailbox, and folder need to be set per recipient */
666  ast_string_field_set(&recording_data, context, recipient->context);
667  ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
668  ast_string_field_set(&recording_data, folder, recipient->folder);
669 
670  ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
671  recording_data.context);
672  ast_app_copy_recording_to_vm(&recording_data);
673  }
674 
675  /* Free the string fields for recording_data before exiting the function. */
676  ast_string_field_free_memory(&recording_data);
677 }
678 
679 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
680 {
681  /* Initialize the file if not already done so */
682  char *last_slash = NULL;
683  if (!ast_strlen_zero(filename)) {
684  if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
685  *oflags = O_CREAT | O_WRONLY;
686  *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
687 
688  last_slash = strrchr(filename, '/');
689 
690  if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
691  **ext = '\0';
692  *ext = *ext + 1;
693  } else {
694  *ext = "raw";
695  }
696 
697  if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
698  ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
699  *errflag = 1;
700  } else {
701  struct ast_filestream *tmp = *fs;
702  mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_get_sample_rate(tmp->fmt->format));
703  }
704  }
705  }
706 }
707 
708 static int mixmonitor_autochan_is_bridged(struct ast_autochan *autochan)
709 {
710  int is_bridged;
711 
712  ast_autochan_channel_lock(autochan);
713  is_bridged = ast_channel_is_bridged(autochan->chan);
714  ast_autochan_channel_unlock(autochan);
715  return is_bridged;
716 }
717 
718 static void *mixmonitor_thread(void *obj)
719 {
720  struct mixmonitor *mixmonitor = obj;
721  char *fs_ext = "";
722  char *fs_read_ext = "";
723  char *fs_write_ext = "";
724 
725  struct ast_filestream **fs = NULL;
726  struct ast_filestream **fs_read = NULL;
727  struct ast_filestream **fs_write = NULL;
728 
729  unsigned int oflags;
730  int errflag = 0;
731  struct ast_format *format_slin;
732 
733  /* Keep callid association before any log messages */
734  if (mixmonitor->callid) {
735  ast_callid_threadassoc_add(mixmonitor->callid);
736  }
737 
738  ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
739 
740  fs = &mixmonitor->mixmonitor_ds->fs;
741  fs_read = &mixmonitor->mixmonitor_ds->fs_read;
742  fs_write = &mixmonitor->mixmonitor_ds->fs_write;
743 
744  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
745  mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
746  mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
747  mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
748 
749  format_slin = ast_format_cache_get_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate);
750 
751  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
752 
753  /* The audiohook must enter and exit the loop locked */
754  ast_audiohook_lock(&mixmonitor->audiohook);
755  while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
756  struct ast_frame *fr = NULL;
757  struct ast_frame *fr_read = NULL;
758  struct ast_frame *fr_write = NULL;
759 
760  if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, format_slin,
761  &fr_read, &fr_write))) {
762  ast_audiohook_trigger_wait(&mixmonitor->audiohook);
763 
764  if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
765  break;
766  }
767  continue;
768  }
769 
770  /* audiohook lock is not required for the next block.
771  * Unlock it, but remember to lock it before looping or exiting */
772  ast_audiohook_unlock(&mixmonitor->audiohook);
773 
774  if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED)
775  || mixmonitor_autochan_is_bridged(mixmonitor->autochan)) {
776  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
777 
778  /* Write out the frame(s) */
779  if ((*fs_read) && (fr_read)) {
780  struct ast_frame *cur;
781 
782  for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
783  ast_writestream(*fs_read, cur);
784  }
785  }
786 
787  if ((*fs_write) && (fr_write)) {
788  struct ast_frame *cur;
789 
790  for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
791  ast_writestream(*fs_write, cur);
792  }
793  }
794 
795  if ((*fs) && (fr)) {
796  struct ast_frame *cur;
797 
798  for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
799  ast_writestream(*fs, cur);
800  }
801  }
802  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
803  }
804  /* All done! free it. */
805  if (fr) {
806  ast_frame_free(fr, 0);
807  }
808  if (fr_read) {
809  ast_frame_free(fr_read, 0);
810  }
811  if (fr_write) {
812  ast_frame_free(fr_write, 0);
813  }
814 
815  fr = NULL;
816  fr_write = NULL;
817  fr_read = NULL;
818 
819  ast_audiohook_lock(&mixmonitor->audiohook);
820  }
821 
822  ast_audiohook_unlock(&mixmonitor->audiohook);
823 
824  if (ast_test_flag(mixmonitor, MUXFLAG_BEEP_STOP)) {
825  ast_autochan_channel_lock(mixmonitor->autochan);
826  ast_stream_and_wait(mixmonitor->autochan->chan, "beep", "");
827  ast_autochan_channel_unlock(mixmonitor->autochan);
828  }
829 
830  ast_autochan_destroy(mixmonitor->autochan);
831 
832  /* Datastore cleanup. close the filestream and wait for ds destruction */
833  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
834  mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
835  if (!mixmonitor->mixmonitor_ds->destruction_ok) {
836  ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
837  }
838  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
839 
840  /* kill the audiohook */
841  destroy_monitor_audiohook(mixmonitor);
842 
843  if (mixmonitor->post_process) {
844  ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
845  ast_safe_system(mixmonitor->post_process);
846  }
847 
848  ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
849  ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
850 
851  if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
852  if (ast_strlen_zero(fs_ext)) {
853  ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
854  mixmonitor -> name);
855  } else {
856  ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
857  copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
858  }
859  if (!ast_strlen_zero(fs_read_ext)) {
860  ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
861  copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
862  }
863  if (!ast_strlen_zero(fs_write_ext)) {
864  ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
865  copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
866  }
867  } else {
868  ast_debug(3, "No recipients to forward monitor to, moving on.\n");
869  }
870 
871  if (ast_test_flag(mixmonitor, MUXFLAG_AUTO_DELETE)) {
872  ast_debug(3, "Deleting our copies of recording files\n");
873  if (!ast_strlen_zero(fs_ext)) {
874  ast_filedelete(mixmonitor->filename, fs_ext);
875  }
876  if (!ast_strlen_zero(fs_read_ext)) {
877  ast_filedelete(mixmonitor->filename_read, fs_ext);
878  }
879  if (!ast_strlen_zero(fs_write_ext)) {
880  ast_filedelete(mixmonitor->filename_write, fs_ext);
881  }
882  }
883 
884  mixmonitor_free(mixmonitor);
885 
887  return NULL;
888 }
889 
890 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
891 {
892  struct ast_datastore *datastore = NULL;
893  struct mixmonitor_ds *mixmonitor_ds;
894 
895  if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
896  return -1;
897  }
898 
899  if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
900  ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
901  ast_free(mixmonitor_ds);
902  return -1;
903  }
904 
905  ast_mutex_init(&mixmonitor_ds->lock);
906  ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
907 
908  if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
909  ast_mutex_destroy(&mixmonitor_ds->lock);
910  ast_cond_destroy(&mixmonitor_ds->destruction_condition);
911  ast_free(mixmonitor_ds);
912  return -1;
913  }
914 
915  if (ast_test_flag(mixmonitor, MUXFLAG_BEEP_START)) {
916  ast_autochan_channel_lock(mixmonitor->autochan);
917  ast_stream_and_wait(mixmonitor->autochan->chan, "beep", "");
918  ast_autochan_channel_unlock(mixmonitor->autochan);
919  }
920 
921  mixmonitor_ds->samp_rate = 8000;
922  mixmonitor_ds->audiohook = &mixmonitor->audiohook;
923  mixmonitor_ds->filename = ast_strdup(mixmonitor->filename);
924  if (!ast_strlen_zero(beep_id)) {
925  mixmonitor_ds->beep_id = ast_strdup(beep_id);
926  }
927  datastore->data = mixmonitor_ds;
928 
929  ast_channel_lock(chan);
930  ast_channel_datastore_add(chan, datastore);
931  ast_channel_unlock(chan);
932 
933  mixmonitor->mixmonitor_ds = mixmonitor_ds;
934  return 0;
935 }
936 
937 static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
938 {
939  struct ast_datastore *datastore;
940 
941  ast_channel_lock(chan);
942 
943  datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
944 
945  /*
946  * Currently the one place this function is called from guarantees a
947  * datastore is present, thus return checks can be avoided here.
948  */
949  ast_channel_datastore_remove(chan, datastore);
950  ast_datastore_free(datastore);
951 
952  ast_channel_unlock(chan);
953 }
954 
955 static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
956  unsigned int flags, int readvol, int writevol,
957  const char *post_process, const char *filename_write,
958  char *filename_read, const char *uid_channel_var,
959  const char *recipients, const char *beep_id)
960 {
961  pthread_t thread;
962  struct mixmonitor *mixmonitor;
963  char postprocess2[1024] = "";
964  char *datastore_id = NULL;
965 
966  postprocess2[0] = 0;
967  /* If a post process system command is given attach it to the structure */
968  if (!ast_strlen_zero(post_process)) {
969  char *p1, *p2;
970 
971  p1 = ast_strdupa(post_process);
972  for (p2 = p1; *p2; p2++) {
973  if (*p2 == '^' && *(p2+1) == '{') {
974  *p2 = '$';
975  }
976  }
977  ast_channel_lock(chan);
978  pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
979  ast_channel_unlock(chan);
980  }
981 
982  /* Pre-allocate mixmonitor structure and spy */
983  if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
984  return -1;
985  }
986 
987  /* Now that the struct has been calloced, go ahead and initialize the string fields. */
988  if (ast_string_field_init(mixmonitor, 512)) {
989  mixmonitor_free(mixmonitor);
990  return -1;
991  }
992 
993  /* Setup the actual spy before creating our thread */
994  if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
995  mixmonitor_free(mixmonitor);
996  return -1;
997  }
998 
999  /* Copy over flags and channel name */
1000  mixmonitor->flags = flags;
1001  if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1002  mixmonitor_free(mixmonitor);
1003  return -1;
1004  }
1005 
1006  if (!ast_strlen_zero(filename)) {
1007  mixmonitor->filename = ast_strdup(filename);
1008  }
1009 
1010  if (!ast_strlen_zero(filename_write)) {
1011  mixmonitor->filename_write = ast_strdup(filename_write);
1012  }
1013 
1014  if (!ast_strlen_zero(filename_read)) {
1015  mixmonitor->filename_read = ast_strdup(filename_read);
1016  }
1017 
1018  if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1019  ast_autochan_destroy(mixmonitor->autochan);
1020  mixmonitor_free(mixmonitor);
1021  ast_free(datastore_id);
1022  return -1;
1023  }
1024 
1025  if (!ast_strlen_zero(uid_channel_var)) {
1026  if (datastore_id) {
1027  pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1028  }
1029  }
1030 
1031  mixmonitor->name = ast_strdup(ast_channel_name(chan));
1032 
1033  if (!ast_strlen_zero(postprocess2)) {
1034  mixmonitor->post_process = ast_strdup(postprocess2);
1035  }
1036 
1037  if (!ast_strlen_zero(recipients)) {
1038  char callerid[256];
1039 
1040  ast_channel_lock(chan);
1041 
1042  /* We use the connected line of the invoking channel for caller ID,
1043  * unless we've been told to use the Caller ID.
1044  * The initial use for this relied on Connected Line to get the
1045  * actual number for recording with Digium phones,
1046  * but in generic use the Caller ID is likely what people want.
1047  */
1048 
1049  if (ast_test_flag(mixmonitor, MUXFLAG_REAL_CALLERID)) {
1050  struct ast_party_caller *caller;
1051  caller = ast_channel_caller(chan);
1052  ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1053  caller->id.name.str, caller->id.number.valid,
1054  caller->id.number.str);
1055  ast_callerid_merge(callerid, sizeof(callerid),
1056  S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1057  S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1058  "Unknown");
1059  } else {
1060  struct ast_party_connected_line *connected;
1061  connected = ast_channel_connected(chan);
1062  ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1063  connected->id.name.str, connected->id.number.valid,
1064  connected->id.number.str);
1065  ast_callerid_merge(callerid, sizeof(callerid),
1066  S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1067  S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1068  "Unknown");
1069  }
1070 
1071  ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
1072  ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1073  ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1074  ast_string_field_set(mixmonitor, call_callerid, callerid);
1075  mixmonitor->call_priority = ast_channel_priority(chan);
1076 
1077  ast_channel_unlock(chan);
1078 
1079  add_vm_recipients_from_string(mixmonitor, recipients);
1080  }
1081 
1082  ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
1083  if (!ast_test_flag(mixmonitor, MUXFLAG_NO_RWSYNC)) {
1084  ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_SUBSTITUTE_SILENCE);
1085  }
1086 
1087  if (readvol)
1088  mixmonitor->audiohook.options.read_volume = readvol;
1089  if (writevol)
1090  mixmonitor->audiohook.options.write_volume = writevol;
1091 
1092  if (startmon(chan, &mixmonitor->audiohook)) {
1093  ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1094  mixmonitor_spy_type, ast_channel_name(chan));
1095  mixmonitor_ds_remove_and_free(chan, datastore_id);
1096  ast_free(datastore_id);
1097  ast_autochan_destroy(mixmonitor->autochan);
1098  ast_audiohook_destroy(&mixmonitor->audiohook);
1099  mixmonitor_free(mixmonitor);
1100  return -1;
1101  }
1102 
1103  ast_free(datastore_id);
1104 
1105  /* reference be released at mixmonitor destruction */
1106  mixmonitor->callid = ast_read_threadstorage_callid();
1107 
1108  return ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
1109 }
1110 
1111 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1112 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1113 static char *filename_parse(char *filename, char *buffer, size_t len)
1114 {
1115  char *slash;
1116  char *ext;
1117 
1118  ast_assert(len > 0);
1119 
1120  if (ast_strlen_zero(filename)) {
1121  ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1122  buffer[0] = 0;
1123  return buffer;
1124  }
1125 
1126  /* If we don't have an absolute path, make one */
1127  if (*filename != '/') {
1128  char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1129  sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1130  filename = build;
1131  }
1132 
1133  ast_copy_string(buffer, filename, len);
1134 
1135  /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1136  match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1137  ends up referring to a file that does not/will not exist */
1138  ext = strrchr(buffer, '.');
1139  if (ext && !strcmp(ext, ".wav49")) {
1140  /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1141  * so this is safe */
1142  memcpy(ext, ".WAV", sizeof(".WAV"));
1143  }
1144 
1145  if ((slash = strrchr(filename, '/'))) {
1146  *slash = '\0';
1147  }
1148  ast_mkdir(filename, 0777);
1149 
1150  return buffer;
1151 }
1152 
1153 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1154 {
1155  int x, readvol = 0, writevol = 0;
1156  char *filename_read = NULL;
1157  char *filename_write = NULL;
1158  char filename_buffer[1024] = "";
1159  char *uid_channel_var = NULL;
1160  char beep_id[64] = "";
1161 
1162  struct ast_flags flags = { 0 };
1163  char *recipients = NULL;
1164  char *parse;
1165  RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
1166  AST_DECLARE_APP_ARGS(args,
1167  AST_APP_ARG(filename);
1168  AST_APP_ARG(options);
1169  AST_APP_ARG(post_process);
1170  );
1171 
1172  if (ast_strlen_zero(data)) {
1173  ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1174  return -1;
1175  }
1176 
1177  parse = ast_strdupa(data);
1178 
1179  AST_STANDARD_APP_ARGS(args, parse);
1180 
1181  if (args.options) {
1182  char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1183 
1184  ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
1185 
1186  if (ast_test_flag(&flags, MUXFLAG_DEPRECATED_RWSYNC)) {
1187  ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1188  " and does not need to be specified.\n");
1189  }
1190 
1191  if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
1192  if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
1193  ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1194  } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1195  ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1196  } else {
1197  readvol = get_volfactor(x);
1198  }
1199  }
1200 
1201  if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
1202  if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
1203  ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1204  } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1205  ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1206  } else {
1207  writevol = get_volfactor(x);
1208  }
1209  }
1210 
1211  if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
1212  if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1213  ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1214  } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1215  ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1216  } else {
1217  readvol = writevol = get_volfactor(x);
1218  }
1219  }
1220 
1221  if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
1222  if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
1223  ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1224  } else {
1225  recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1226  }
1227  }
1228 
1229  if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
1230  filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1231  }
1232 
1233  if (ast_test_flag(&flags, MUXFLAG_READ)) {
1234  filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1235  }
1236 
1237  if (ast_test_flag(&flags, MUXFLAG_UID)) {
1238  uid_channel_var = opts[OPT_ARG_UID];
1239  }
1240 
1241  if (ast_test_flag(&flags, MUXFLAG_BEEP)) {
1242  const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1243  unsigned int interval = 15;
1244 
1245  if (sscanf(interval_str, "%30u", &interval) != 1) {
1246  ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1247  interval_str, interval);
1248  }
1249 
1250  if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1251  ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1252  return -1;
1253  }
1254  }
1255  }
1256  /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1257 
1258  if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
1259  ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1260  return -1;
1261  }
1262 
1263  /* If filename exists, try to create directories for it */
1264  if (!(ast_strlen_zero(args.filename))) {
1265  args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1266  }
1267 
1268  pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1269 
1270  /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1272  if (launch_monitor_thread(chan,
1273  args.filename,
1274  flags.flags,
1275  readvol,
1276  writevol,
1277  args.post_process,
1278  filename_write,
1279  filename_read,
1280  uid_channel_var,
1281  recipients,
1282  beep_id)) {
1284  }
1285 
1286  message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
1288  if (message) {
1290  }
1291 
1292  return 0;
1293 }
1294 
1295 static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1296 {
1297  struct ast_datastore *datastore = NULL;
1298  char *parse = "";
1299  struct mixmonitor_ds *mixmonitor_ds;
1300  const char *beep_id = NULL;
1301  RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
1302 
1303  AST_DECLARE_APP_ARGS(args,
1304  AST_APP_ARG(mixmonid);
1305  );
1306 
1307  if (!ast_strlen_zero(data)) {
1308  parse = ast_strdupa(data);
1309  }
1310 
1311  AST_STANDARD_APP_ARGS(args, parse);
1312 
1313  ast_channel_lock(chan);
1314 
1315  datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info,
1316  S_OR(args.mixmonid, NULL));
1317  if (!datastore) {
1318  ast_channel_unlock(chan);
1319  return -1;
1320  }
1321  mixmonitor_ds = datastore->data;
1322 
1323  ast_mutex_lock(&mixmonitor_ds->lock);
1324 
1325  /* closing the filestream here guarantees the file is available to the dialplan
1326  * after calling StopMixMonitor */
1327  mixmonitor_ds_close_fs(mixmonitor_ds);
1328 
1329  /* The mixmonitor thread may be waiting on the audiohook trigger.
1330  * In order to exit from the mixmonitor loop before waiting on channel
1331  * destruction, poke the audiohook trigger. */
1332  if (mixmonitor_ds->audiohook) {
1333  if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
1335  }
1336  ast_audiohook_lock(mixmonitor_ds->audiohook);
1337  ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
1338  ast_audiohook_unlock(mixmonitor_ds->audiohook);
1339  mixmonitor_ds->audiohook = NULL;
1340  }
1341 
1342  if (!ast_strlen_zero(mixmonitor_ds->beep_id)) {
1343  beep_id = ast_strdupa(mixmonitor_ds->beep_id);
1344  }
1345 
1346  ast_mutex_unlock(&mixmonitor_ds->lock);
1347 
1348  /* Remove the datastore so the monitor thread can exit */
1349  if (!ast_channel_datastore_remove(chan, datastore)) {
1350  ast_datastore_free(datastore);
1351  }
1352 
1353  ast_channel_unlock(chan);
1354 
1355  if (!ast_strlen_zero(beep_id)) {
1356  ast_beep_stop(chan, beep_id);
1357  }
1358 
1359  message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
1361  NULL);
1362  if (message) {
1364  }
1365 
1366  return 0;
1367 }
1368 
1369 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1370 {
1371  stop_mixmonitor_full(chan, data);
1372  return 0;
1373 }
1374 
1375 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1376 {
1377  struct ast_channel *chan;
1378  struct ast_datastore *datastore = NULL;
1379  struct mixmonitor_ds *mixmonitor_ds = NULL;
1380 
1381  switch (cmd) {
1382  case CLI_INIT:
1383  e->command = "mixmonitor {start|stop|list}";
1384  e->usage =
1385  "Usage: mixmonitor start <chan_name> [args]\n"
1386  " The optional arguments are passed to the MixMonitor application.\n"
1387  " mixmonitor stop <chan_name> [args]\n"
1388  " The optional arguments are passed to the StopMixMonitor application.\n"
1389  " mixmonitor list <chan_name>\n";
1390  return NULL;
1391  case CLI_GENERATE:
1392  return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1393  }
1394 
1395  if (a->argc < 3) {
1396  return CLI_SHOWUSAGE;
1397  }
1398 
1399  if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1400  ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1401  /* Technically this is a failure, but we don't want 2 errors printing out */
1402  return CLI_SUCCESS;
1403  }
1404 
1405  if (!strcasecmp(a->argv[1], "start")) {
1406  mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1407  } else if (!strcasecmp(a->argv[1], "stop")){
1408  stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1409  } else if (!strcasecmp(a->argv[1], "list")) {
1410  ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1411  ast_cli(a->fd, "=========================================================================\n");
1412  ast_channel_lock(chan);
1413  AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1414  if (datastore->info == &mixmonitor_ds_info) {
1415  char *filename = "";
1416  char *filename_read = "";
1417  char *filename_write = "";
1418 
1419  mixmonitor_ds = datastore->data;
1420  if (mixmonitor_ds->fs) {
1421  filename = mixmonitor_ds->fs->filename;
1422  }
1423  if (mixmonitor_ds->fs_read) {
1424  filename_read = mixmonitor_ds->fs_read->filename;
1425  }
1426  if (mixmonitor_ds->fs_write) {
1427  filename_write = mixmonitor_ds->fs_write->filename;
1428  }
1429  ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1430  }
1431  }
1432  ast_channel_unlock(chan);
1433  } else {
1434  chan = ast_channel_unref(chan);
1435  return CLI_SHOWUSAGE;
1436  }
1437 
1438  chan = ast_channel_unref(chan);
1439 
1440  return CLI_SUCCESS;
1441 }
1442 
1443 /*! \brief Mute / unmute an individual MixMonitor by id */
1444 static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1445  enum ast_audiohook_flags flag, int clearmute)
1446 {
1447  struct ast_datastore *datastore = NULL;
1448  char *parse = "";
1449  struct mixmonitor_ds *mixmonitor_ds;
1450 
1451  AST_DECLARE_APP_ARGS(args,
1452  AST_APP_ARG(mixmonid);
1453  );
1454 
1455  if (!ast_strlen_zero(data)) {
1456  parse = ast_strdupa(data);
1457  }
1458 
1459  AST_STANDARD_APP_ARGS(args, parse);
1460 
1461  ast_channel_lock(chan);
1462 
1463  datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info,
1464  S_OR(args.mixmonid, NULL));
1465  if (!datastore) {
1466  ast_channel_unlock(chan);
1467  return -1;
1468  }
1469  mixmonitor_ds = datastore->data;
1470 
1471  ast_mutex_lock(&mixmonitor_ds->lock);
1472 
1473  if (mixmonitor_ds->audiohook) {
1474  if (clearmute) {
1475  ast_clear_flag(mixmonitor_ds->audiohook, flag);
1476  } else {
1477  ast_set_flag(mixmonitor_ds->audiohook, flag);
1478  }
1479  }
1480 
1481  ast_mutex_unlock(&mixmonitor_ds->lock);
1482  ast_channel_unlock(chan);
1483 
1484  return 0;
1485 }
1486 
1487 /*! \brief Mute / unmute a MixMonitor channel */
1488 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1489 {
1490  struct ast_channel *c;
1491  const char *name = astman_get_header(m, "Channel");
1492  const char *id = astman_get_header(m, "ActionID");
1493  const char *state = astman_get_header(m, "State");
1494  const char *direction = astman_get_header(m,"Direction");
1495  const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1496  int clearmute = 1, mutedcount = 0;
1497  enum ast_audiohook_flags flag;
1498  RAII_VAR(struct stasis_message *, stasis_message, NULL, ao2_cleanup);
1499  RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1500 
1501  if (ast_strlen_zero(direction)) {
1502  astman_send_error(s, m, "No direction specified. Must be read, write or both");
1503  return AMI_SUCCESS;
1504  }
1505 
1506  if (!strcasecmp(direction, "read")) {
1507  flag = AST_AUDIOHOOK_MUTE_READ;
1508  } else if (!strcasecmp(direction, "write")) {
1509  flag = AST_AUDIOHOOK_MUTE_WRITE;
1510  } else if (!strcasecmp(direction, "both")) {
1512  } else {
1513  astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1514  return AMI_SUCCESS;
1515  }
1516 
1517  if (ast_strlen_zero(name)) {
1518  astman_send_error(s, m, "No channel specified");
1519  return AMI_SUCCESS;
1520  }
1521 
1522  if (ast_strlen_zero(state)) {
1523  astman_send_error(s, m, "No state specified");
1524  return AMI_SUCCESS;
1525  }
1526 
1527  clearmute = ast_false(state);
1528 
1529  c = ast_channel_get_by_name(name);
1530  if (!c) {
1531  astman_send_error(s, m, "No such channel");
1532  return AMI_SUCCESS;
1533  }
1534 
1535  if (ast_strlen_zero(mixmonitor_id)) {
1536  mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1537  if (mutedcount < 0) {
1538  ast_channel_unref(c);
1539  astman_send_error(s, m, "Cannot set mute flag");
1540  return AMI_SUCCESS;
1541  }
1542  } else {
1543  if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1544  ast_channel_unref(c);
1545  astman_send_error(s, m, "Cannot set mute flag");
1546  return AMI_SUCCESS;
1547  }
1548  mutedcount = 1;
1549  }
1550 
1551 
1552  stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1553  "direction", direction,
1554  "state", ast_true(state),
1555  "mixmonitorid", mixmonitor_id,
1556  "count", mutedcount);
1557 
1558  stasis_message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(c),
1559  ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1560 
1561  if (stasis_message) {
1563  }
1564 
1565  astman_append(s, "Response: Success\r\n");
1566 
1567  if (!ast_strlen_zero(id)) {
1568  astman_append(s, "ActionID: %s\r\n", id);
1569  }
1570 
1571  astman_append(s, "\r\n");
1572 
1573  ast_channel_unref(c);
1574 
1575  return AMI_SUCCESS;
1576 }
1577 
1578 static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1579 {
1580  char args[PATH_MAX];
1581 
1582  if (ast_strlen_zero(options)) {
1583  snprintf(args, sizeof(args), "%s", filename);
1584  } else {
1585  snprintf(args, sizeof(args), "%s,%s", filename, options);
1586  }
1587 
1588  return mixmonitor_exec(chan, args);
1589 }
1590 
1591 static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1592 {
1593  return stop_mixmonitor_full(chan, mixmonitor_id);
1594 }
1595 
1596 static int manager_mixmonitor(struct mansession *s, const struct message *m)
1597 {
1598  struct ast_channel *c;
1599  const char *name = astman_get_header(m, "Channel");
1600  const char *id = astman_get_header(m, "ActionID");
1601  const char *file = astman_get_header(m, "File");
1602  const char *options = astman_get_header(m, "Options");
1603  const char *command = astman_get_header(m, "Command");
1604  char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1605  struct ast_flags flags = { 0 };
1606  char *uid_channel_var = NULL;
1607  const char *mixmonitor_id = NULL;
1608  int res;
1609  char args[PATH_MAX];
1610 
1611  if (ast_strlen_zero(name)) {
1612  astman_send_error(s, m, "No channel specified");
1613  return AMI_SUCCESS;
1614  }
1615 
1616  c = ast_channel_get_by_name(name);
1617  if (!c) {
1618  astman_send_error(s, m, "No such channel");
1619  return AMI_SUCCESS;
1620  }
1621 
1622  if (!ast_strlen_zero(options)) {
1623  ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
1624  }
1625 
1626  snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1627 
1628  res = mixmonitor_exec(c, args);
1629 
1630  if (ast_test_flag(&flags, MUXFLAG_UID)) {
1631  uid_channel_var = opts[OPT_ARG_UID];
1632  ast_channel_lock(c);
1633  mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1634  mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1635  ast_channel_unlock(c);
1636  }
1637 
1638  if (res) {
1639  ast_channel_unref(c);
1640  astman_send_error(s, m, "Could not start monitoring channel");
1641  return AMI_SUCCESS;
1642  }
1643 
1644  astman_append(s, "Response: Success\r\n");
1645 
1646  if (!ast_strlen_zero(id)) {
1647  astman_append(s, "ActionID: %s\r\n", id);
1648  }
1649 
1650  if (!ast_strlen_zero(mixmonitor_id)) {
1651  astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1652  }
1653 
1654  astman_append(s, "\r\n");
1655 
1656  ast_channel_unref(c);
1657 
1658  return AMI_SUCCESS;
1659 }
1660 
1661 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1662 {
1663  struct ast_channel *c;
1664  const char *name = astman_get_header(m, "Channel");
1665  const char *id = astman_get_header(m, "ActionID");
1666  const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1667  int res;
1668 
1669  if (ast_strlen_zero(name)) {
1670  astman_send_error(s, m, "No channel specified");
1671  return AMI_SUCCESS;
1672  }
1673 
1674  c = ast_channel_get_by_name(name);
1675  if (!c) {
1676  astman_send_error(s, m, "No such channel");
1677  return AMI_SUCCESS;
1678  }
1679 
1680  res = stop_mixmonitor_full(c, mixmonitor_id);
1681  if (res) {
1682  ast_channel_unref(c);
1683  astman_send_error(s, m, "Could not stop monitoring channel");
1684  return AMI_SUCCESS;
1685  }
1686 
1687  astman_append(s, "Response: Success\r\n");
1688 
1689  if (!ast_strlen_zero(id)) {
1690  astman_append(s, "ActionID: %s\r\n", id);
1691  }
1692 
1693  astman_append(s, "\r\n");
1694 
1695  ast_channel_unref(c);
1696 
1697  return AMI_SUCCESS;
1698 }
1699 
1700 static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1701  char *buf, size_t len)
1702 {
1703  struct ast_datastore *datastore;
1704  struct mixmonitor_ds *ds_data;
1705  AST_DECLARE_APP_ARGS(args,
1706  AST_APP_ARG(id);
1707  AST_APP_ARG(key);
1708  );
1709 
1710  AST_STANDARD_APP_ARGS(args, data);
1711 
1712  if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1713  ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1714  "An ID and key must be provided\n", cmd);
1715  return -1;
1716  }
1717 
1718  ast_channel_lock(chan);
1719  datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1720  ast_channel_unlock(chan);
1721 
1722  if (!datastore) {
1723  ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1724  return -1;
1725  }
1726 
1727  ds_data = datastore->data;
1728 
1729  if (!strcasecmp(args.key, "filename")) {
1730  ast_copy_string(buf, ds_data->filename, len);
1731  } else {
1732  ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
1733  return -1;
1734  }
1735  return 0;
1736 }
1737 
1738 static struct ast_custom_function mixmonitor_function = {
1739  .name = "MIXMONITOR",
1740  .read = func_mixmonitor_read,
1741 };
1742 
1743 static struct ast_cli_entry cli_mixmonitor[] = {
1744  AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1745 };
1746 
1747 static int set_mixmonitor_methods(void)
1748 {
1749  struct ast_mixmonitor_methods mixmonitor_methods = {
1750  .start = start_mixmonitor_callback,
1751  .stop = stop_mixmonitor_callback,
1752  };
1753 
1754  return ast_set_mixmonitor_methods(&mixmonitor_methods);
1755 }
1756 
1757 static int clear_mixmonitor_methods(void)
1758 {
1760 }
1761 
1762 static int unload_module(void)
1763 {
1764  int res;
1765 
1766  ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1767  res = ast_unregister_application(stop_app);
1768  res |= ast_unregister_application(app);
1769  res |= ast_manager_unregister("MixMonitorMute");
1770  res |= ast_manager_unregister("MixMonitor");
1771  res |= ast_manager_unregister("StopMixMonitor");
1772  res |= ast_custom_function_unregister(&mixmonitor_function);
1773  res |= clear_mixmonitor_methods();
1774 
1775  return res;
1776 }
1777 
1778 static int load_module(void)
1779 {
1780  int res;
1781 
1782  ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1783  res = ast_register_application_xml(app, mixmonitor_exec);
1784  res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
1785  res |= ast_manager_register_xml("MixMonitorMute", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, manager_mute_mixmonitor);
1786  res |= ast_manager_register_xml("MixMonitor", EVENT_FLAG_SYSTEM, manager_mixmonitor);
1787  res |= ast_manager_register_xml("StopMixMonitor", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, manager_stop_mixmonitor);
1788  res |= ast_custom_function_register(&mixmonitor_function);
1789  res |= set_mixmonitor_methods();
1790 
1791  return res;
1792 }
1793 
1794 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
1795  .support_level = AST_MODULE_SUPPORT_CORE,
1796  .load = load_module,
1797  .unload = unload_module,
1798  .optional_modules = "func_periodic_hook",
1799 );
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data, enum ast_audiohook_flags flag, int clearmute)
Mute / unmute an individual MixMonitor by id.
Main Channel structure associated with a channel.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
ast_audiohook_flags
Definition: audiohook.h:54
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
#define ast_autochan_channel_lock(autochan)
Lock the autochan's channel lock.
Definition: autochan.h:75
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
struct ast_party_id id
Connected party ID.
Definition: channel.h:458
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_party_name name
Subscriber name.
Definition: channel.h:340
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
Audiohooks Architecture.
void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
Update audiohook's status.
Definition: audiohook.c:540
pthread_t thread
Definition: app_sla.c:329
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
Test Framework API.
Structure for a data store type.
Definition: datastore.h:31
char * str
Subscriber name (Malloced)
Definition: channel.h:264
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition: logger.c:2320
Definition of a media format.
Definition: format.c:43
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:484
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
ast_callid ast_read_threadstorage_callid(void)
extracts the callerid from the thread
Definition: logger.c:2298
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
Mute / unmute a MixMonitor channel.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
int ast_set_mixmonitor_methods(struct ast_mixmonitor_methods *vmethod_table)
Setup MixMonitor virtual methods table. Use this to provide the MixMonitor functionality from a loada...
Definition: mixmonitor.c:43
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:124
Periodic beeps into the audio of a call.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Header for providers of file and format handling routines. Clients of these routines should include "...
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
struct ast_channel * ast_channel_get_by_name_prefix(const char *name, size_t name_len)
Find a channel by a name prefix.
Definition: channel.c:1434
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
Definition: audiohook.c:100
MixMonitor virtual methods table definition.
Definition: mixmonitor.h:58
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
Definition: audiohook.h:318
struct ast_module * self
Definition: module.h:356
int ast_audiohook_set_mute_all(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
Mute frames read from or written for all audiohooks on a channel.
Definition: audiohook.c:1380
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define AST_MAX_EXTENSION
Definition: channel.h:134
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
struct ast_format_def * fmt
Definition: mod_format.h:103
Caller Party information.
Definition: channel.h:418
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1785
const struct ast_datastore_info * info
Definition: datastore.h:67
A set of macros to manage forward-linked lists.
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
struct ast_format * format
Definition: mod_format.h:48
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Core PBX routines and definitions.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
"smart" channels that update automatically if a channel is masqueraded
struct stasis_message_type * ast_channel_mixmonitor_start_type(void)
Message type for starting mixmonitor on a channel.
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:550
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
struct stasis_message_type * ast_channel_mixmonitor_stop_type(void)
Message type for stopping mixmonitor on a channel.
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Connected Line/Party information.
Definition: channel.h:456
const ast_string_field name
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
Definition: file.c:1423
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:457
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition: channel.c:10545
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
#define AST_MAX_CONTEXT
Definition: channel.h:135
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1878
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
Structure used to handle boolean flags.
Definition: utils.h:199
ast_cond_t trigger
Definition: audiohook.h:106
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
const char * usage
Definition: cli.h:177
void ast_autochan_destroy(struct ast_autochan *autochan)
destroy an ast_autochan structure
Definition: autochan.c:64
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:483
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_clear_mixmonitor_methods(void)
Clear the MixMonitor virtual methods table. Use this to cleanup function pointers provided by a modul...
Definition: mixmonitor.c:59
struct ast_autochan * ast_autochan_setup(struct ast_channel *chan)
set up a new ast_autochan structure
Definition: autochan.c:38
void * data
Definition: datastore.h:66
struct ast_audiohook_options options
Definition: audiohook.h:119
Structure used for ast_copy_recording_to_vm in order to cleanly supply data needed for making the rec...
struct ast_frame * ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame)
Reads a frame in from the audiohook structure in mixed audio mode and copies read and write frame dat...
Definition: audiohook.c:451
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
unsigned int flags
Definition: audiohook.h:111
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
Definition: file.c:244
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
char * ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Command completion for the list of active channels.
Definition: main/cli.c:1865
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
loadable MixMonitor functionality
Definition: search.h:40
enum ast_audiohook_status status
Definition: audiohook.h:108
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:279
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
#define ast_audiohook_lock(ah)
Lock an audiohook.
Definition: audiohook.h:313
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
Wait for audiohook trigger to be triggered.
Definition: audiohook.c:1094
int ast_app_copy_recording_to_vm(struct ast_vm_recording_data *vm_rec_data)
param[in] vm_rec_data Contains data needed to make the recording. retval 0 voicemail successfully cre...
Definition: main/app.c:596
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Structure for mutex and tracking information.
Definition: lock.h:135
struct ast_format * ast_format_cache_get_slin_by_rate(unsigned int rate)
Retrieve the best signed linear format given a sample rate.
Definition: format_cache.c:512
void ast_frame_free(struct ast_frame *frame, int cache)
Frees a frame or list of frames.
Definition: main/frame.c:176
Media Format Cache API.
#define AST_APP_ARG(name)
Define an application argument.
struct stasis_message_type * ast_channel_mixmonitor_mute_type(void)
Message type for muting or unmuting mixmonitor on a channel.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342