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