Asterisk - The Open Source Telephony Project GIT-master-9647a4f
Loading...
Searching...
No Matches
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
135 going to the channel and output it as a 2 channel (stereo)
136 raw stream rather than mixing it. You must use the
137 <literal>.raw</literal> file extension. Any other extension
138 will produce a corrupted file.</para>
139 </option>
140 <option name="n">
141 <para>When the <replaceable>r</replaceable> or <replaceable>t</replaceable> option is
142 used, MixMonitor will insert silence into the specified files to maintain
143 synchronization between them. Use this option to disable that behavior.</para>
144 </option>
145 <option name="i">
146 <argument name="chanvar" required="true" />
147 <para>Stores the MixMonitor's ID on this channel variable.</para>
148 </option>
149 <option name="p">
150 <para>Play a beep on the channel that starts the recording.</para>
151 </option>
152 <option name="P">
153 <para>Play a beep on the channel that stops the recording.</para>
154 </option>
155 <option name="m">
156 <argument name="mailbox" required="true" />
157 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
158 separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
159 the syntax: mailbox@context/folder</para>
160 </option>
161 <option name="s">
162 <argument name="seconds" required="true" />
163 <para>Don't record until <replaceable>seconds</replaceable> (can be fractional) have elapsed since MixMonitor was invoked.
164 No audio is written to the recording file during this time. If the call ends before this period,
165 no audio will be saved. This can be useful to avoid recording announcements,
166 ringback tones, or other non-essential early audio.</para>
167 </option>
168 </optionlist>
169 </parameter>
170 <parameter name="command">
171 <para>Will be executed when the recording is over.</para>
172 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
173 <para>All variables will be evaluated at the time MixMonitor is called.</para>
174 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
175 or <variable>CALLERID(name)</variable> as part of the command parameters. You
176 risk a command injection attack executing arbitrary commands if the untrusted
177 strings aren't filtered to remove dangerous characters. See function
178 <variable>FILTER()</variable>.</para></warning>
179 </parameter>
180 </syntax>
181 <description>
182 <para>Records the audio on the current channel to the specified file.</para>
183 <para>This application does not automatically answer and should be preceded by
184 an application such as Answer or Progress().</para>
185 <note><para>MixMonitor runs as an audiohook.</para></note>
186 <note><para>If a filename passed to MixMonitor ends with
187 <literal>.wav49</literal>, Asterisk will silently convert the extension to
188 <literal>.WAV</literal> for legacy reasons. <variable>MIXMONITOR_FILENAME</variable>
189 will contain the actual filename that Asterisk is writing to, not necessarily the
190 value that was passed in.</para></note>
191 <variablelist>
192 <variable name="MIXMONITOR_FILENAME">
193 <para>Will contain the filename used to record.</para>
194 </variable>
195 </variablelist>
196 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
197 or <variable>CALLERID(name)</variable> as part of ANY of the application's
198 parameters. You risk a command injection attack executing arbitrary commands
199 if the untrusted strings aren't filtered to remove dangerous characters. See
200 function <variable>FILTER()</variable>.</para></warning>
201 <warning><para>When using the <literal>D</literal> option to save
202 interleaved audio, you MUST use <literal>.raw</literal> as the
203 file extension. Any other extension will produce a corrupted file.</para></warning>
204 </description>
205 <see-also>
206 <ref type="application">StopMixMonitor</ref>
207 </see-also>
208 </application>
209 <application name="StopMixMonitor" language="en_US">
210 <since>
211 <version>1.4.0</version>
212 </since>
213 <synopsis>
214 Stop recording a call through MixMonitor, and free the recording's file handle.
215 </synopsis>
216 <syntax>
217 <parameter name="MixMonitorID" required="false">
218 <para>If a valid ID is provided, then this command will stop only that specific
219 MixMonitor.</para>
220 </parameter>
221 </syntax>
222 <description>
223 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
224 on the current channel.</para>
225 </description>
226 <see-also>
227 <ref type="application">MixMonitor</ref>
228 </see-also>
229 </application>
230 <manager name="MixMonitorMute" language="en_US">
231 <since>
232 <version>1.8.0</version>
233 </since>
234 <synopsis>
235 Mute / unMute a Mixmonitor recording.
236 </synopsis>
237 <syntax>
238 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
239 <parameter name="Channel" required="true">
240 <para>Used to specify the channel to mute.</para>
241 </parameter>
242 <parameter name="Direction">
243 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
244 </parameter>
245 <parameter name="State">
246 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
247 </parameter>
248 </syntax>
249 <description>
250 <para>This action may be used to mute a MixMonitor recording.</para>
251 </description>
252 </manager>
253 <manager name="MixMonitor" language="en_US">
254 <since>
255 <version>11.0.0</version>
256 </since>
257 <synopsis>
258 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
259 to guarantee the audio file is available for processing during dialplan execution.
260 </synopsis>
261 <syntax>
262 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
263 <parameter name="Channel" required="true">
264 <para>Used to specify the channel to record.</para>
265 </parameter>
266 <parameter name="File">
267 <para>Is the name of the file created in the monitor spool directory.
268 Defaults to the same name as the channel (with slashes replaced with dashes).
269 This argument is optional if you specify to record unidirectional audio with
270 either the r(filename) or t(filename) options in the options field. If
271 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
272 be recorded.</para>
273 </parameter>
274 <parameter name="Options">
275 <para>Options that apply to the MixMonitor in the same way as they
276 would apply if invoked from the MixMonitor application. For a list of
277 available options, see the documentation for the mixmonitor application. </para>
278 </parameter>
279 <parameter name="Command">
280 <para>Will be executed when the recording is over.
281 Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
282 All variables will be evaluated at the time MixMonitor is called.</para>
283 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
284 or <variable>CALLERID(name)</variable> as part of the command parameters. You
285 risk a command injection attack executing arbitrary commands if the untrusted
286 strings aren't filtered to remove dangerous characters. See function
287 <variable>FILTER()</variable>.</para></warning>
288 </parameter>
289 </syntax>
290 <description>
291 <para>This action records the audio on the current channel to the specified file.</para>
292 <variablelist>
293 <variable name="MIXMONITOR_FILENAME">
294 <para>Will contain the filename used to record the mixed stream.</para>
295 </variable>
296 </variablelist>
297 </description>
298 </manager>
299 <manager name="StopMixMonitor" language="en_US">
300 <since>
301 <version>11.0.0</version>
302 </since>
303 <synopsis>
304 Stop recording a call through MixMonitor, and free the recording's file handle.
305 </synopsis>
306 <syntax>
307 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
308 <parameter name="Channel" required="true">
309 <para>The name of the channel monitored.</para>
310 </parameter>
311 <parameter name="MixMonitorID" required="false">
312 <para>If a valid ID is provided, then this command will stop only that specific
313 MixMonitor.</para>
314 </parameter>
315 </syntax>
316 <description>
317 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
318 action on the current channel.</para>
319 </description>
320 </manager>
321 <function name="MIXMONITOR" language="en_US">
322 <since>
323 <version>13.0.0</version>
324 </since>
325 <synopsis>
326 Retrieve data pertaining to specific instances of MixMonitor on a channel.
327 </synopsis>
328 <syntax>
329 <parameter name="id" required="true">
330 <para>The unique ID of the MixMonitor instance. The unique ID can be retrieved through the channel
331 variable used as an argument to the <replaceable>i</replaceable> option to MixMonitor.</para>
332 </parameter>
333 <parameter name="key" required="true">
334 <para>The piece of data to retrieve from the MixMonitor.</para>
335 <enumlist>
336 <enum name="filename" />
337 </enumlist>
338 </parameter>
339 </syntax>
340 </function>
341 <managerEvent language="en_US" name="MixMonitorStart">
342 <managerEventInstance class="EVENT_FLAG_CALL">
343 <since>
344 <version>16.17.0</version>
345 <version>18.3.0</version>
346 </since>
347 <synopsis>Raised when monitoring has started on a channel.</synopsis>
348 <syntax>
349 <channel_snapshot/>
350 </syntax>
351 <see-also>
352 <ref type="managerEvent">MixMonitorStop</ref>
353 <ref type="application">MixMonitor</ref>
354 <ref type="manager">MixMonitor</ref>
355 </see-also>
356 </managerEventInstance>
357 </managerEvent>
358 <managerEvent language="en_US" name="MixMonitorStop">
359 <managerEventInstance class="EVENT_FLAG_CALL">
360 <since>
361 <version>16.17.0</version>
362 <version>18.3.0</version>
363 </since>
364 <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
365 <syntax>
366 <channel_snapshot/>
367 </syntax>
368 <see-also>
369 <ref type="managerEvent">MixMonitorStart</ref>
370 <ref type="application">StopMixMonitor</ref>
371 <ref type="manager">StopMixMonitor</ref>
372 </see-also>
373 </managerEventInstance>
374 </managerEvent>
375 <managerEvent language="en_US" name="MixMonitorMute">
376 <managerEventInstance class="EVENT_FLAG_CALL">
377 <since>
378 <version>16.17.0</version>
379 <version>18.3.0</version>
380 </since>
381 <synopsis>Raised when monitoring is muted or unmuted on a channel.</synopsis>
382 <syntax>
383 <channel_snapshot/>
384 <parameter name="Direction">
385 <para>Which part of the recording was muted or unmuted: read, write or both
386 (from channel, to channel or both directions).</para>
387 </parameter>
388 <parameter name="State">
389 <para>If the monitoring was muted or unmuted: 1 when muted, 0 when unmuted.</para>
390 </parameter>
391 </syntax>
392 <see-also>
393 <ref type="manager">MixMonitorMute</ref>
394 </see-also>
395 </managerEventInstance>
396 </managerEvent>
397
398
399 ***/
400
401#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
402
403#define MIN_SKIP_SECONDS 1
404
405static const char * const app = "MixMonitor";
406
407static const char * const stop_app = "StopMixMonitor";
408
409static const char * const mixmonitor_spy_type = "MixMonitor";
410
411/*!
412 * \internal
413 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
414 */
421
424 char *filename;
428 char *name;
430 unsigned int flags;
433
434 /* the below string fields describe data used for creating voicemails from the recording */
440 );
442
443 /* Number of seconds (can be fractional) to skip at the start of recording */
445
446 /* FUTURE DEVELOPMENT NOTICE
447 * recipient_list will need locks if we make it editable after the monitor is started */
449};
450
472
487
507});
508
510 unsigned int destruction_ok;
513
514 /* The filestream is held in the datastore so it can be stopped
515 * immediately during stop_mixmonitor or channel destruction. */
517
521
523
524 unsigned int samp_rate;
525 char *filename;
526 char *beep_id;
527};
528
529/*!
530 * \internal
531 * \pre mixmonitor_ds must be locked before calling this function
532 */
534{
535 unsigned char quitting = 0;
536
537 if (mixmonitor_ds->fs) {
538 quitting = 1;
541 ast_verb(2, "MixMonitor close filestream (mixed)\n");
542 }
543
544 if (mixmonitor_ds->fs_read) {
545 quitting = 1;
548 ast_verb(2, "MixMonitor close filestream (read)\n");
549 }
550
551 if (mixmonitor_ds->fs_write) {
552 quitting = 1;
555 ast_verb(2, "MixMonitor close filestream (write)\n");
556 }
557
558 if (quitting) {
560 }
561}
562
575
577 .type = "mixmonitor",
578 .destroy = mixmonitor_ds_destroy,
579};
580
594
595static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
596{
597 if (!chan) {
598 return -1;
599 }
600
601 return ast_audiohook_attach(chan, audiohook);
602}
603
604/*!
605 * \internal
606 * \brief adds recipients to a mixmonitor's recipient list
607 * \param mixmonitor mixmonitor being affected
608 * \param vm_recipients string containing the desired recipients to add
609 */
610static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
611{
612 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
613 char *cur_mailbox = ast_strdupa(vm_recipients);
614 char *cur_context;
615 char *cur_folder;
616 char *next;
617 int elements_processed = 0;
618
619 while (!ast_strlen_zero(cur_mailbox)) {
620 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
621 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
622 *(next++) = '\0';
623 }
624
625 if ((cur_folder = strchr(cur_mailbox, '/'))) {
626 *(cur_folder++) = '\0';
627 } else {
628 cur_folder = "INBOX";
629 }
630
631 if ((cur_context = strchr(cur_mailbox, '@'))) {
632 *(cur_context++) = '\0';
633 } else {
634 cur_context = "default";
635 }
636
637 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
638 struct vm_recipient *recipient;
639 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
640 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
641 return;
642 }
643 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
644 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
645 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
646
647 /* Add to list */
648 ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
650 } else {
651 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
652 }
653
654 cur_mailbox = next;
655 elements_processed++;
656 }
657}
658
660{
661 struct vm_recipient *current;
663 /* Clear list element data */
665 }
666}
667
668#define SAMPLES_PER_FRAME 160
669
694
695/*!
696 * \internal
697 * \brief Copies the mixmonitor to all voicemail recipients
698 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
699 * \param ext Format of the file that was saved
700 * \param filename
701 */
702static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
703{
704 struct vm_recipient *recipient = NULL;
707 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
708 return;
709 }
710
711 /* Copy strings to stringfields that will be used for all recipients */
718 /* and call_priority gets copied too */
719 recording_data.call_priority = mixmonitor->call_priority;
720
721 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
722 /* context, mailbox, and folder need to be set per recipient */
726
727 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
728 recording_data.context);
730 }
731
732 /* Free the string fields for recording_data before exiting the function. */
734}
735
736static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
737{
738 /* Initialize the file if not already done so */
739 char *last_slash = NULL;
740 if (!ast_strlen_zero(filename)) {
741 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
742 *oflags = O_CREAT | O_WRONLY;
743 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
744
745 last_slash = strrchr(filename, '/');
746
747 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
748 **ext = '\0';
749 *ext = *ext + 1;
750 } else {
751 *ext = "raw";
752 }
753
754 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
755 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
756 *errflag = 1;
757 } else {
758 struct ast_filestream *tmp = *fs;
760 }
761 }
762 }
763}
764
766{
767 int is_bridged;
768
770 is_bridged = ast_channel_is_bridged(autochan->chan);
772 return is_bridged;
773}
774
775static void *mixmonitor_thread(void *obj)
776{
777 struct mixmonitor *mixmonitor = obj;
778 char *fs_ext = "";
779 char *fs_read_ext = "";
780 char *fs_write_ext = "";
781
782 struct ast_filestream **fs = NULL;
783 struct ast_filestream **fs_read = NULL;
784 struct ast_filestream **fs_write = NULL;
785
786 unsigned int oflags;
787 int errflag = 0;
788 struct ast_format *format_slin;
789
790 struct timeval skip_start = ast_tvnow();
791
792 /* Keep callid association before any log messages */
793 if (mixmonitor->callid) {
795 }
796
797 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
798
800 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
801 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
802
804 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
805 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
806 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
807
809
811
812 if (mixmonitor->skip_seconds > 0.0) {
813 ast_debug(3, "%s skipping initial %.3f seconds\n",
815 }
816
817 /* The audiohook must enter and exit the loop locked */
820 struct ast_frame *fr = NULL;
821 struct ast_frame *fr_read = NULL;
822 struct ast_frame *fr_write = NULL;
823
825 &fr_read, &fr_write))) {
827
829 break;
830 }
831 continue;
832 }
833
834 /* audiohook lock is not required for the next block.
835 * Unlock it, but remember to lock it before looping or exiting */
837
841
842 /* Skip writing audio for the first N seconds */
843 if (mixmonitor->skip_seconds > 0.0) {
844 struct timeval now = ast_tvnow();
845 double elapsed = ast_tvdiff_ms(now, skip_start) / 1000.0;
846
847 if (elapsed < mixmonitor->skip_seconds) {
849 /* Skip this frame and continue */
850 goto frame_cleanup;
851 } else {
852 ast_debug(3, "%s skip period %.3f seconds elapsed; starting to write audio\n",
855 }
856 }
857
858 /* Write out the frame(s) */
859 if ((*fs_read) && (fr_read)) {
860 struct ast_frame *cur;
861
862 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
863 ast_writestream(*fs_read, cur);
864 }
865 }
866
867 if ((*fs_write) && (fr_write)) {
868 struct ast_frame *cur;
869
870 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
871 ast_writestream(*fs_write, cur);
872 }
873 }
874
876 /* The 'D' option is set, so mix the frame as an interleaved dual channel frame */
877 int i;
878 short read_buf[SAMPLES_PER_FRAME];
880 short stereo_buf[SAMPLES_PER_FRAME * 2];
881 struct ast_frame stereo_frame = {
883 .datalen = sizeof(stereo_buf),
885 };
886
887 if (fr) {
888 ast_frame_free(fr, 0);
889 fr = NULL;
890 }
891
892 if (fr_read) {
893 memcpy(read_buf, fr_read->data.ptr, sizeof(read_buf));
894 } else {
895 memset(read_buf, 0, sizeof(read_buf));
896 }
897
898 if (fr_write) {
899 memcpy(write_buf, fr_write->data.ptr, sizeof(write_buf));
900 } else {
901 memset(write_buf, 0, sizeof(write_buf));
902 }
903
904 for (i = 0; i < SAMPLES_PER_FRAME; i++) {
905 stereo_buf[i * 2] = read_buf[i];
906 stereo_buf[i * 2 + 1] = write_buf[i];
907 }
908
909 stereo_frame.data.ptr = stereo_buf;
911
912 fr = ast_frdup(&stereo_frame);
913 }
914
915 if ((*fs) && (fr)) {
916 struct ast_frame *cur;
917
918 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
919 ast_writestream(*fs, cur);
920 }
921 }
923 }
924
925frame_cleanup:
926 /* All done! free it. */
927 if (fr) {
928 ast_frame_free(fr, 0);
929 }
930 if (fr_read) {
931 ast_frame_free(fr_read, 0);
932 }
933 if (fr_write) {
934 ast_frame_free(fr_write, 0);
935 }
936
937 fr = NULL;
938 fr_write = NULL;
939 fr_read = NULL;
940
942 }
943
945
950 }
951
953
954 /* Datastore cleanup. close the filestream and wait for ds destruction */
959 }
961
962 /* kill the audiohook */
964
966 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
968 }
969
970 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
971 ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
972
974 if (ast_strlen_zero(fs_ext)) {
975 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
976 mixmonitor -> name);
977 } else {
978 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
980 }
981 if (!ast_strlen_zero(fs_read_ext)) {
982 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
984 }
985 if (!ast_strlen_zero(fs_write_ext)) {
986 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
988 }
989 } else {
990 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
991 }
992
994 ast_debug(3, "Deleting our copies of recording files\n");
995 if (!ast_strlen_zero(fs_ext)) {
997 }
998 if (!ast_strlen_zero(fs_read_ext)) {
1000 }
1001 if (!ast_strlen_zero(fs_write_ext)) {
1003 }
1004 }
1005
1007
1009 return NULL;
1010}
1011
1012static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
1013{
1014 struct ast_datastore *datastore = NULL;
1016
1017 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
1018 return -1;
1019 }
1020
1021 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
1022 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
1024 return -1;
1025 }
1026
1029
1030 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
1034 return -1;
1035 }
1036
1041 }
1042
1043 mixmonitor_ds->samp_rate = 8000;
1046 if (!ast_strlen_zero(beep_id)) {
1048 }
1049 datastore->data = mixmonitor_ds;
1050
1051 ast_channel_lock(chan);
1052 ast_channel_datastore_add(chan, datastore);
1053 ast_channel_unlock(chan);
1054
1056 return 0;
1057}
1058
1059static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
1060{
1061 struct ast_datastore *datastore;
1062
1063 ast_channel_lock(chan);
1064
1065 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
1066
1067 /*
1068 * Currently the one place this function is called from guarantees a
1069 * datastore is present, thus return checks can be avoided here.
1070 */
1071 ast_channel_datastore_remove(chan, datastore);
1072 ast_datastore_free(datastore);
1073
1074 ast_channel_unlock(chan);
1075}
1076
1077static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
1078 unsigned int flags, int readvol, int writevol,
1079 const char *post_process, const char *filename_write,
1080 char *filename_read, const char *uid_channel_var,
1081 const char *recipients, const char *beep_id, double skip_seconds)
1082{
1083 pthread_t thread;
1084 struct mixmonitor *mixmonitor;
1085 char postprocess2[1024] = "";
1086 char *datastore_id = NULL;
1087
1088 postprocess2[0] = 0;
1089 /* If a post process system command is given attach it to the structure */
1091 char *p1, *p2;
1092
1094 for (p2 = p1; *p2; p2++) {
1095 if (*p2 == '^' && *(p2+1) == '{') {
1096 *p2 = '$';
1097 }
1098 }
1099 ast_channel_lock(chan);
1100 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
1101 ast_channel_unlock(chan);
1102 }
1103
1104 /* Pre-allocate mixmonitor structure and spy */
1105 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
1106 return -1;
1107 }
1108
1109 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
1112 return -1;
1113 }
1114
1115 /* Setup the actual spy before creating our thread */
1118 return -1;
1119 }
1120
1121 /* Copy over flags and channel name */
1124 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1126 return -1;
1127 }
1128
1129 if (!ast_strlen_zero(filename)) {
1131 }
1132
1135 }
1136
1139 }
1140
1141 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1144 ast_free(datastore_id);
1145 return -1;
1146 }
1147
1148 if (!ast_strlen_zero(uid_channel_var)) {
1149 if (datastore_id) {
1150 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1151 }
1152 }
1153
1155
1156 if (!ast_strlen_zero(postprocess2)) {
1157 mixmonitor->post_process = ast_strdup(postprocess2);
1158 }
1159
1160 if (!ast_strlen_zero(recipients)) {
1161 char callerid[256];
1162
1163 ast_channel_lock(chan);
1164
1165 /* We use the connected line of the invoking channel for caller ID,
1166 * unless we've been told to use the Caller ID.
1167 * The initial use for this relied on Connected Line to get the
1168 * actual number for recording with Digium phones,
1169 * but in generic use the Caller ID is likely what people want.
1170 */
1171
1173 struct ast_party_caller *caller;
1174 caller = ast_channel_caller(chan);
1175 ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1176 caller->id.name.str, caller->id.number.valid,
1177 caller->id.number.str);
1178 ast_callerid_merge(callerid, sizeof(callerid),
1179 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1180 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1181 "Unknown");
1182 } else {
1185 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1186 connected->id.name.str, connected->id.number.valid,
1187 connected->id.number.str);
1188 ast_callerid_merge(callerid, sizeof(callerid),
1189 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1190 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1191 "Unknown");
1192 }
1193
1195 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1196 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1197 ast_string_field_set(mixmonitor, call_callerid, callerid);
1199
1200 ast_channel_unlock(chan);
1201
1203 }
1204
1208 }
1209
1210 if (readvol)
1212 if (writevol)
1214
1215 if (startmon(chan, &mixmonitor->audiohook)) {
1216 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1218 mixmonitor_ds_remove_and_free(chan, datastore_id);
1219 ast_free(datastore_id);
1223 return -1;
1224 }
1225
1226 ast_free(datastore_id);
1227
1228 /* reference be released at mixmonitor destruction */
1230
1232}
1233
1234/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1235/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1236static char *filename_parse(char *filename, char *buffer, size_t len)
1237{
1238 char *slash;
1239 char *ext;
1240
1241 ast_assert(len > 0);
1242
1243 if (ast_strlen_zero(filename)) {
1244 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1245 buffer[0] = 0;
1246 return buffer;
1247 }
1248
1249 /* If we don't have an absolute path, make one */
1250 if (*filename != '/') {
1251 char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1252 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1253 filename = build;
1254 }
1255
1256 ast_copy_string(buffer, filename, len);
1257
1258 /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1259 match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1260 ends up referring to a file that does not/will not exist */
1261 ext = strrchr(buffer, '.');
1262 if (ext && !strcmp(ext, ".wav49")) {
1263 /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1264 * so this is safe */
1265 memcpy(ext, ".WAV", sizeof(".WAV"));
1266 }
1267
1268 if ((slash = strrchr(filename, '/'))) {
1269 *slash = '\0';
1270 }
1271 ast_mkdir(filename, 0777);
1272
1273 return buffer;
1274}
1275
1276static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1277{
1278 int x, readvol = 0, writevol = 0;
1279 double skip_seconds = 0.0;
1280 char *filename_read = NULL;
1281 char *filename_write = NULL;
1282 char filename_buffer[1024] = "";
1283 char *uid_channel_var = NULL;
1284 char beep_id[64] = "";
1285
1286 struct ast_flags flags = { 0 };
1287 char *recipients = NULL;
1288 char *parse;
1291 AST_APP_ARG(filename);
1293 AST_APP_ARG(post_process);
1294 );
1295
1296 if (ast_strlen_zero(data)) {
1297 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1298 return -1;
1299 }
1300
1301 parse = ast_strdupa(data);
1302
1304
1305 if (args.options) {
1306 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1307
1309
1311 ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1312 " and does not need to be specified.\n");
1313 }
1314
1317 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1318 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1319 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1320 } else {
1321 readvol = get_volfactor(x);
1322 }
1323 }
1324
1327 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1328 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1329 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1330 } else {
1331 writevol = get_volfactor(x);
1332 }
1333 }
1334
1336 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1337 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1338 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1339 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1340 } else {
1341 readvol = writevol = get_volfactor(x);
1342 }
1343 }
1344
1347 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1348 } else {
1349 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1350 }
1351 }
1352
1354 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1355 }
1356
1358 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1359 }
1360
1362 uid_channel_var = opts[OPT_ARG_UID];
1363 }
1364
1366 const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1367 unsigned int interval = 15;
1368
1369 if (sscanf(interval_str, "%30u", &interval) != 1) {
1370 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1371 interval_str, interval);
1372 }
1373
1374 if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1375 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1376 return -1;
1377 }
1378 }
1379
1381 if (ast_strlen_zero(opts[OPT_ARG_SKIP])) {
1382 ast_log(LOG_WARNING, "No skip value provided for the 's' (skip) option; skipping will be ignored as no default exists.\n");
1383 } else {
1384 char *endptr = NULL;
1385 double val = strtod(opts[OPT_ARG_SKIP], &endptr);
1386 if (endptr == opts[OPT_ARG_SKIP] || *endptr != '\0') {
1387 ast_log(LOG_WARNING, "Skip value '%s' is not a valid number; ignoring skip.\n", opts[OPT_ARG_SKIP]);
1388 } else if (val < (double) MIN_SKIP_SECONDS) {
1389 ast_log(LOG_WARNING, "Skip value %.3f is below minimum %d; ignoring skip.\n", val, MIN_SKIP_SECONDS);
1390 } else {
1391 skip_seconds = val;
1392 }
1393 }
1394 }
1395 }
1396 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1397
1399 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1400 return -1;
1401 }
1402
1403 /* If filename exists, try to create directories for it */
1404 if (!(ast_strlen_zero(args.filename))) {
1405 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1406 }
1407
1408 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1409
1410 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1412 if (launch_monitor_thread(chan,
1413 args.filename,
1414 flags.flags,
1415 readvol,
1416 writevol,
1417 args.post_process,
1418 filename_write,
1419 filename_read,
1420 uid_channel_var,
1421 recipients,
1422 beep_id,
1423 skip_seconds)) {
1425 }
1426
1429 if (message) {
1431 }
1432
1433 return 0;
1434}
1435
1436static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1437{
1438 struct ast_datastore *datastore = NULL;
1439 char *parse = "";
1441 const char *beep_id = NULL;
1443
1445 AST_APP_ARG(mixmonid);
1446 );
1447
1448 if (!ast_strlen_zero(data)) {
1449 parse = ast_strdupa(data);
1450 }
1451
1453
1454 ast_channel_lock(chan);
1455
1457 S_OR(args.mixmonid, NULL));
1458 if (!datastore) {
1459 ast_channel_unlock(chan);
1460 return -1;
1461 }
1462 mixmonitor_ds = datastore->data;
1463
1465
1466 /* closing the filestream here guarantees the file is available to the dialplan
1467 * after calling StopMixMonitor */
1469
1470 /* The mixmonitor thread may be waiting on the audiohook trigger.
1471 * In order to exit from the mixmonitor loop before waiting on channel
1472 * destruction, poke the audiohook trigger. */
1473 if (mixmonitor_ds->audiohook) {
1476 }
1481 }
1482
1485 }
1486
1488
1489 /* Remove the datastore so the monitor thread can exit */
1490 if (!ast_channel_datastore_remove(chan, datastore)) {
1491 ast_datastore_free(datastore);
1492 }
1493
1494 ast_channel_unlock(chan);
1495
1496 if (!ast_strlen_zero(beep_id)) {
1497 ast_beep_stop(chan, beep_id);
1498 }
1499
1502 NULL);
1503 if (message) {
1505 }
1506
1507 return 0;
1508}
1509
1510static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1511{
1512 stop_mixmonitor_full(chan, data);
1513 return 0;
1514}
1515
1516static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1517{
1518 struct ast_channel *chan;
1519 struct ast_datastore *datastore = NULL;
1521
1522 switch (cmd) {
1523 case CLI_INIT:
1524 e->command = "mixmonitor {start|stop|list}";
1525 e->usage =
1526 "Usage: mixmonitor start <chan_name> [args]\n"
1527 " The optional arguments are passed to the MixMonitor application.\n"
1528 " mixmonitor stop <chan_name> [args]\n"
1529 " The optional arguments are passed to the StopMixMonitor application.\n"
1530 " mixmonitor list <chan_name>\n";
1531 return NULL;
1532 case CLI_GENERATE:
1533 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1534 }
1535
1536 if (a->argc < 3) {
1537 return CLI_SHOWUSAGE;
1538 }
1539
1540 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1541 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1542 /* Technically this is a failure, but we don't want 2 errors printing out */
1543 return CLI_SUCCESS;
1544 }
1545
1546 if (!strcasecmp(a->argv[1], "start")) {
1547 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1548 } else if (!strcasecmp(a->argv[1], "stop")){
1549 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1550 } else if (!strcasecmp(a->argv[1], "list")) {
1551 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1552 ast_cli(a->fd, "=========================================================================\n");
1553 ast_channel_lock(chan);
1554 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1555 if (datastore->info == &mixmonitor_ds_info) {
1556 char *filename = "";
1557 char *filename_read = "";
1558 char *filename_write = "";
1559
1560 mixmonitor_ds = datastore->data;
1561 if (mixmonitor_ds->fs) {
1563 }
1564 if (mixmonitor_ds->fs_read) {
1565 filename_read = mixmonitor_ds->fs_read->filename;
1566 }
1567 if (mixmonitor_ds->fs_write) {
1568 filename_write = mixmonitor_ds->fs_write->filename;
1569 }
1570 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1571 }
1572 }
1573 ast_channel_unlock(chan);
1574 } else {
1575 chan = ast_channel_unref(chan);
1576 return CLI_SHOWUSAGE;
1577 }
1578
1579 chan = ast_channel_unref(chan);
1580
1581 return CLI_SUCCESS;
1582}
1583
1584/*! \brief Mute / unmute an individual MixMonitor by id */
1585static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1586 enum ast_audiohook_flags flag, int clearmute)
1587{
1588 struct ast_datastore *datastore = NULL;
1589 char *parse = "";
1591
1593 AST_APP_ARG(mixmonid);
1594 );
1595
1596 if (!ast_strlen_zero(data)) {
1597 parse = ast_strdupa(data);
1598 }
1599
1601
1602 ast_channel_lock(chan);
1603
1605 S_OR(args.mixmonid, NULL));
1606 if (!datastore) {
1607 ast_channel_unlock(chan);
1608 return -1;
1609 }
1610 mixmonitor_ds = datastore->data;
1611
1613
1614 if (mixmonitor_ds->audiohook) {
1615 if (clearmute) {
1617 } else {
1619 }
1620 }
1621
1623 ast_channel_unlock(chan);
1624
1625 return 0;
1626}
1627
1628/*! \brief Mute / unmute a MixMonitor channel */
1629static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1630{
1631 struct ast_channel *c;
1632 const char *name = astman_get_header(m, "Channel");
1633 const char *id = astman_get_header(m, "ActionID");
1634 const char *state = astman_get_header(m, "State");
1635 const char *direction = astman_get_header(m,"Direction");
1636 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1637 int clearmute = 1, mutedcount = 0;
1640 RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1641
1643 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1644 return AMI_SUCCESS;
1645 }
1646
1647 if (!strcasecmp(direction, "read")) {
1649 } else if (!strcasecmp(direction, "write")) {
1651 } else if (!strcasecmp(direction, "both")) {
1653 } else {
1654 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1655 return AMI_SUCCESS;
1656 }
1657
1658 if (ast_strlen_zero(name)) {
1659 astman_send_error(s, m, "No channel specified");
1660 return AMI_SUCCESS;
1661 }
1662
1663 if (ast_strlen_zero(state)) {
1664 astman_send_error(s, m, "No state specified");
1665 return AMI_SUCCESS;
1666 }
1667
1668 clearmute = ast_false(state);
1669
1671 if (!c) {
1672 astman_send_error(s, m, "No such channel");
1673 return AMI_SUCCESS;
1674 }
1675
1676 if (ast_strlen_zero(mixmonitor_id)) {
1677 mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1678 if (mutedcount < 0) {
1680 astman_send_error(s, m, "Cannot set mute flag");
1681 return AMI_SUCCESS;
1682 }
1683 } else {
1684 if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1686 astman_send_error(s, m, "Cannot set mute flag");
1687 return AMI_SUCCESS;
1688 }
1689 mutedcount = 1;
1690 }
1691
1692
1693 stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1694 "direction", direction,
1695 "state", ast_true(state),
1696 "mixmonitorid", mixmonitor_id,
1697 "count", mutedcount);
1698
1700 ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1701
1702 if (stasis_message) {
1704 }
1705
1706 astman_append(s, "Response: Success\r\n");
1707
1708 if (!ast_strlen_zero(id)) {
1709 astman_append(s, "ActionID: %s\r\n", id);
1710 }
1711
1712 astman_append(s, "\r\n");
1713
1715
1716 return AMI_SUCCESS;
1717}
1718
1719static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1720{
1721 char args[PATH_MAX];
1722
1723 if (ast_strlen_zero(options)) {
1724 snprintf(args, sizeof(args), "%s", filename);
1725 } else {
1726 snprintf(args, sizeof(args), "%s,%s", filename, options);
1727 }
1728
1729 return mixmonitor_exec(chan, args);
1730}
1731
1732static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1733{
1734 return stop_mixmonitor_full(chan, mixmonitor_id);
1735}
1736
1737static int manager_mixmonitor(struct mansession *s, const struct message *m)
1738{
1739 struct ast_channel *c;
1740 const char *name = astman_get_header(m, "Channel");
1741 const char *id = astman_get_header(m, "ActionID");
1742 const char *file = astman_get_header(m, "File");
1743 const char *options = astman_get_header(m, "Options");
1744 const char *command = astman_get_header(m, "Command");
1745 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1746 struct ast_flags flags = { 0 };
1747 char *uid_channel_var = NULL;
1748 const char *mixmonitor_id = NULL;
1749 int res;
1750 char args[PATH_MAX];
1751
1752 if (ast_strlen_zero(name)) {
1753 astman_send_error(s, m, "No channel specified");
1754 return AMI_SUCCESS;
1755 }
1756
1758 if (!c) {
1759 astman_send_error(s, m, "No such channel");
1760 return AMI_SUCCESS;
1761 }
1762
1763 if (!ast_strlen_zero(options)) {
1765 }
1766
1767 snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1768
1769 res = mixmonitor_exec(c, args);
1770
1772 uid_channel_var = opts[OPT_ARG_UID];
1774 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1775 mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1777 }
1778
1779 if (res) {
1781 astman_send_error(s, m, "Could not start monitoring channel");
1782 return AMI_SUCCESS;
1783 }
1784
1785 astman_append(s, "Response: Success\r\n");
1786
1787 if (!ast_strlen_zero(id)) {
1788 astman_append(s, "ActionID: %s\r\n", id);
1789 }
1790
1791 if (!ast_strlen_zero(mixmonitor_id)) {
1792 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1793 }
1794
1795 astman_append(s, "\r\n");
1796
1798
1799 return AMI_SUCCESS;
1800}
1801
1802static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1803{
1804 struct ast_channel *c;
1805 const char *name = astman_get_header(m, "Channel");
1806 const char *id = astman_get_header(m, "ActionID");
1807 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1808 int res;
1809
1810 if (ast_strlen_zero(name)) {
1811 astman_send_error(s, m, "No channel specified");
1812 return AMI_SUCCESS;
1813 }
1814
1816 if (!c) {
1817 astman_send_error(s, m, "No such channel");
1818 return AMI_SUCCESS;
1819 }
1820
1821 res = stop_mixmonitor_full(c, mixmonitor_id);
1822 if (res) {
1824 astman_send_error(s, m, "Could not stop monitoring channel");
1825 return AMI_SUCCESS;
1826 }
1827
1828 astman_append(s, "Response: Success\r\n");
1829
1830 if (!ast_strlen_zero(id)) {
1831 astman_append(s, "ActionID: %s\r\n", id);
1832 }
1833
1834 astman_append(s, "\r\n");
1835
1837
1838 return AMI_SUCCESS;
1839}
1840
1841static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1842 char *buf, size_t len)
1843{
1844 struct ast_datastore *datastore;
1845 struct mixmonitor_ds *ds_data;
1847 AST_APP_ARG(id);
1848 AST_APP_ARG(key);
1849 );
1850
1852
1853 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1854 ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1855 "An ID and key must be provided\n", cmd);
1856 return -1;
1857 }
1858
1859 ast_channel_lock(chan);
1860 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1861 ast_channel_unlock(chan);
1862
1863 if (!datastore) {
1864 ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1865 return -1;
1866 }
1867
1868 ds_data = datastore->data;
1869
1870 if (!strcasecmp(args.key, "filename")) {
1871 ast_copy_string(buf, ds_data->filename, len);
1872 } else {
1873 ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
1874 return -1;
1875 }
1876 return 0;
1877}
1878
1880 .name = "MIXMONITOR",
1881 .read = func_mixmonitor_read,
1882};
1883
1885 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1886};
1887
1897
1899{
1901}
1902
1903static int unload_module(void)
1904{
1905 int res;
1906
1910 res |= ast_manager_unregister("MixMonitorMute");
1911 res |= ast_manager_unregister("MixMonitor");
1912 res |= ast_manager_unregister("StopMixMonitor");
1915
1916 return res;
1917}
1918
1934
1935AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
1936 .support_level = AST_MODULE_SUPPORT_CORE,
1937 .load = load_module,
1938 .unload = unload_module,
1939 .optional_modules = "func_periodic_hook",
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
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_SKIP
@ 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 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 MIN_SKIP_SECONDS
#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 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, double skip_seconds)
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_SKIP
@ 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:488
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:1131
void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
Update audiohook's status.
Definition audiohook.c:577
#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:1432
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:587
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition audiohook.c:521
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
static int connected
Definition cdr_pgsql.c:73
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:2375
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition channel.c:2384
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:2982
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:1399
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition channel.c:1416
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition channel.c:10593
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3018
#define AST_MAX_CONTEXT
Definition channel.h:135
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
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:2983
#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:2389
size_t current
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#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:1842
@ 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
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:255
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:1457
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:1912
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition file.c:1130
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition file.c:1160
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.
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:1982
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1643
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1903
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7698
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:827
#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.
#define ast_debug(level,...)
Log a DEBUG message.
ast_callid ast_read_threadstorage_callid(void)
extracts the callid from the thread
Definition logger.c:2268
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition logger.c:2290
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).
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
#define ast_cond_destroy(cond)
Definition lock.h:209
#define ast_cond_wait(cond, mutex)
Definition lock.h:212
#define ast_cond_init(cond, attr)
Definition lock.h:208
#define ast_mutex_init(pmutex)
Definition lock.h:193
#define ast_mutex_unlock(a)
Definition lock.h:197
pthread_cond_t ast_cond_t
Definition lock.h:185
#define ast_mutex_destroy(a)
Definition lock.h:195
#define ast_mutex_lock(a)
Definition lock.h:196
#define ast_cond_signal(cond)
Definition lock.h:210
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:156
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:1562
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
static struct @519 args
#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:1578
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
#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:2235
#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:2252
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
struct ast_flags flags
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
Structure used to handle boolean flags.
Definition utils.h:220
unsigned int flags
Definition utils.h:221
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.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@239 data
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:142
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:323
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
struct mixmonitor::@43 recipient_list
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
double skip_seconds
unsigned int flags
struct mixmonitor_ds * mixmonitor_ds
char mailbox[AST_MAX_CONTEXT]
char context[AST_MAX_EXTENSION]
struct vm_recipient::@42 list
struct vm_recipient * next
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition test.h:189
static struct test_options options
static struct test_val a
static struct test_val c
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
#define ast_test_flag(p, flag)
Definition utils.h:64
#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:981
#define ast_assert(a)
Definition utils.h:779
#define ast_clear_flag(p, flag)
Definition utils.h:78
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition utils.c:2515
#define ast_pthread_create_detached_background(a, b, c, d)
Definition utils.h:637
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706
#define MAX(a, b)
Definition utils.h:254