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