Asterisk - The Open Source Telephony Project GIT-master-3dae2cf
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 <synopsis>
67 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
68 to guarantee the audio file is available for processing during dialplan execution.
69 </synopsis>
70 <syntax>
71 <parameter name="file" required="true" argsep=".">
72 <argument name="filename" required="true">
73 <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
74 creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
75 </argument>
76 <argument name="extension" required="true" />
77 </parameter>
78 <parameter name="options">
79 <optionlist>
80 <option name="a">
81 <para>Append to the file instead of overwriting it.</para>
82 </option>
83 <option name="b">
84 <para>Only save audio to the file while the channel is bridged.</para>
85 <note><para>If you utilize this option inside a Local channel, you must make sure the Local
86 channel is not optimized away. To do this, be sure to call your Local channel with the
87 <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
88 </option>
89 <option name="B">
90 <para>Play a periodic beep while this call is being recorded.</para>
91 <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
92 </option>
93 <option name="c">
94 <para>Use the real Caller ID from the channel for the voicemail Caller ID.</para>
95 <para>By default, the Connected Line is used. If you want the channel caller's
96 real number, you may need to specify this option.</para>
97 </option>
98 <option name="d">
99 <para>Delete the recording file as soon as MixMonitor is done with it.</para>
100 <para>For example, if you use the m option to dispatch the recording to a voicemail box,
101 you can specify this option to delete the original copy of it afterwards.</para>
102 </option>
103 <option name="v">
104 <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
105 (range <literal>-4</literal> to <literal>4</literal>)</para>
106 <argument name="x" required="true" />
107 </option>
108 <option name="V">
109 <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
110 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
111 <argument name="x" required="true" />
112 </option>
113 <option name="W">
114 <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
115 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
116 <argument name="x" required="true" />
117 </option>
118 <option name="r">
119 <argument name="file" required="true" />
120 <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
121 Like with the basic filename argument, if an absolute path isn't given, it will create
122 the file in the configured monitoring directory.</para>
123 </option>
124 <option name="t">
125 <argument name="file" required="true" />
126 <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
127 Like with the basic filename argument, if an absolute path isn't given, it will create
128 the file in the configured monitoring directory.</para>
129 </option>
130 <option name="D">
131 <para>Interleave the audio coming from the channel and the audio coming to the channel in
132 the output audio as a dual channel stream, rather than mix it.</para>
133 <note><para>Use .raw as the extension.</para></note>
134 </option>
135 <option name="n">
136 <para>When the <replaceable>r</replaceable> or <replaceable>t</replaceable> option is
137 used, MixMonitor will insert silence into the specified files to maintain
138 synchronization between them. Use this option to disable that behavior.</para>
139 </option>
140 <option name="i">
141 <argument name="chanvar" required="true" />
142 <para>Stores the MixMonitor's ID on this channel variable.</para>
143 </option>
144 <option name="p">
145 <para>Play a beep on the channel that starts the recording.</para>
146 </option>
147 <option name="P">
148 <para>Play a beep on the channel that stops the recording.</para>
149 </option>
150 <option name="m">
151 <argument name="mailbox" required="true" />
152 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
153 separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
154 the syntax: mailbox@context/folder</para>
155 </option>
156 </optionlist>
157 </parameter>
158 <parameter name="command">
159 <para>Will be executed when the recording is over.</para>
160 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
161 <para>All variables will be evaluated at the time MixMonitor is called.</para>
162 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
163 or <variable>CALLERID(name)</variable> as part of the command parameters. You
164 risk a command injection attack executing arbitrary commands if the untrusted
165 strings aren't filtered to remove dangerous characters. See function
166 <variable>FILTER()</variable>.</para></warning>
167 </parameter>
168 </syntax>
169 <description>
170 <para>Records the audio on the current channel to the specified file.</para>
171 <para>This application does not automatically answer and should be preceeded by
172 an application such as Answer or Progress().</para>
173 <note><para>MixMonitor runs as an audiohook.</para></note>
174 <note><para>If a filename passed to MixMonitor ends with
175 <literal>.wav49</literal>, Asterisk will silently convert the extension to
176 <literal>.WAV</literal> for legacy reasons. <variable>MIXMONITOR_FILENAME</variable>
177 will contain the actual filename that Asterisk is writing to, not necessarily the
178 value that was passed in.</para></note>
179 <variablelist>
180 <variable name="MIXMONITOR_FILENAME">
181 <para>Will contain the filename used to record.</para>
182 </variable>
183 </variablelist>
184 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
185 or <variable>CALLERID(name)</variable> as part of ANY of the application's
186 parameters. You risk a command injection attack executing arbitrary commands
187 if the untrusted strings aren't filtered to remove dangerous characters. See
188 function <variable>FILTER()</variable>.</para></warning>
189 </description>
190 <see-also>
191 <ref type="application">StopMixMonitor</ref>
192 </see-also>
193 </application>
194 <application name="StopMixMonitor" language="en_US">
195 <synopsis>
196 Stop recording a call through MixMonitor, and free the recording's file handle.
197 </synopsis>
198 <syntax>
199 <parameter name="MixMonitorID" required="false">
200 <para>If a valid ID is provided, then this command will stop only that specific
201 MixMonitor.</para>
202 </parameter>
203 </syntax>
204 <description>
205 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
206 on the current channel.</para>
207 </description>
208 <see-also>
209 <ref type="application">MixMonitor</ref>
210 </see-also>
211 </application>
212 <manager name="MixMonitorMute" language="en_US">
213 <synopsis>
214 Mute / unMute a Mixmonitor recording.
215 </synopsis>
216 <syntax>
217 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
218 <parameter name="Channel" required="true">
219 <para>Used to specify the channel to mute.</para>
220 </parameter>
221 <parameter name="Direction">
222 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
223 </parameter>
224 <parameter name="State">
225 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
226 </parameter>
227 </syntax>
228 <description>
229 <para>This action may be used to mute a MixMonitor recording.</para>
230 </description>
231 </manager>
232 <manager name="MixMonitor" language="en_US">
233 <synopsis>
234 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
235 to guarantee the audio file is available for processing during dialplan execution.
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 record.</para>
241 </parameter>
242 <parameter name="File">
243 <para>Is the name of the file created in the monitor spool directory.
244 Defaults to the same name as the channel (with slashes replaced with dashes).
245 This argument is optional if you specify to record unidirectional audio with
246 either the r(filename) or t(filename) options in the options field. If
247 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
248 be recorded.</para>
249 </parameter>
250 <parameter name="options">
251 <para>Options that apply to the MixMonitor in the same way as they
252 would apply if invoked from the MixMonitor application. For a list of
253 available options, see the documentation for the mixmonitor application. </para>
254 </parameter>
255 <parameter name="Command">
256 <para>Will be executed when the recording is over.
257 Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
258 All variables will be evaluated at the time MixMonitor is called.</para>
259 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
260 or <variable>CALLERID(name)</variable> as part of the command parameters. You
261 risk a command injection attack executing arbitrary commands if the untrusted
262 strings aren't filtered to remove dangerous characters. See function
263 <variable>FILTER()</variable>.</para></warning>
264 </parameter>
265 </syntax>
266 <description>
267 <para>This action records the audio on the current channel to the specified file.</para>
268 <variablelist>
269 <variable name="MIXMONITOR_FILENAME">
270 <para>Will contain the filename used to record the mixed stream.</para>
271 </variable>
272 </variablelist>
273 </description>
274 </manager>
275 <manager name="StopMixMonitor" language="en_US">
276 <synopsis>
277 Stop recording a call through MixMonitor, and free the recording's file handle.
278 </synopsis>
279 <syntax>
280 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
281 <parameter name="Channel" required="true">
282 <para>The name of the channel monitored.</para>
283 </parameter>
284 <parameter name="MixMonitorID" required="false">
285 <para>If a valid ID is provided, then this command will stop only that specific
286 MixMonitor.</para>
287 </parameter>
288 </syntax>
289 <description>
290 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
291 action on the current channel.</para>
292 </description>
293 </manager>
294 <function name="MIXMONITOR" language="en_US">
295 <synopsis>
296 Retrieve data pertaining to specific instances of MixMonitor on a channel.
297 </synopsis>
298 <syntax>
299 <parameter name="id" required="true">
300 <para>The unique ID of the MixMonitor instance. The unique ID can be retrieved through the channel
301 variable used as an argument to the <replaceable>i</replaceable> option to MixMonitor.</para>
302 </parameter>
303 <parameter name="key" required="true">
304 <para>The piece of data to retrieve from the MixMonitor.</para>
305 <enumlist>
306 <enum name="filename" />
307 </enumlist>
308 </parameter>
309 </syntax>
310 </function>
311 <managerEvent language="en_US" name="MixMonitorStart">
312 <managerEventInstance class="EVENT_FLAG_CALL">
313 <synopsis>Raised when monitoring has started on a channel.</synopsis>
314 <syntax>
315 <channel_snapshot/>
316 </syntax>
317 <see-also>
318 <ref type="managerEvent">MixMonitorStop</ref>
319 <ref type="application">MixMonitor</ref>
320 <ref type="manager">MixMonitor</ref>
321 </see-also>
322 </managerEventInstance>
323 </managerEvent>
324 <managerEvent language="en_US" name="MixMonitorStop">
325 <managerEventInstance class="EVENT_FLAG_CALL">
326 <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
327 <syntax>
328 <channel_snapshot/>
329 </syntax>
330 <see-also>
331 <ref type="managerEvent">MixMonitorStart</ref>
332 <ref type="application">StopMixMonitor</ref>
333 <ref type="manager">StopMixMonitor</ref>
334 </see-also>
335 </managerEventInstance>
336 </managerEvent>
337 <managerEvent language="en_US" name="MixMonitorMute">
338 <managerEventInstance class="EVENT_FLAG_CALL">
339 <synopsis>Raised when monitoring is muted or unmuted on a channel.</synopsis>
340 <syntax>
341 <channel_snapshot/>
342 <parameter name="Direction">
343 <para>Which part of the recording was muted or unmuted: read, write or both
344 (from channel, to channel or both directions).</para>
345 </parameter>
346 <parameter name="State">
347 <para>If the monitoring was muted or unmuted: 1 when muted, 0 when unmuted.</para>
348 </parameter>
349 </syntax>
350 <see-also>
351 <ref type="manager">MixMonitorMute</ref>
352 </see-also>
353 </managerEventInstance>
354 </managerEvent>
355
356
357 ***/
358
359#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
360
361static const char * const app = "MixMonitor";
362
363static const char * const stop_app = "StopMixMonitor";
364
365static const char * const mixmonitor_spy_type = "MixMonitor";
366
367/*!
368 * \internal
369 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
370 */
374 char folder[80];
376};
377
380 char *filename;
384 char *name;
386 unsigned int flags;
389
390 /* the below string fields describe data used for creating voicemails from the recording */
396 );
398
399 /* FUTURE DEVELOPMENT NOTICE
400 * recipient_list will need locks if we make it editable after the monitor is started */
402};
403
405 MUXFLAG_APPEND = (1 << 1),
406 MUXFLAG_BRIDGED = (1 << 2),
407 MUXFLAG_VOLUME = (1 << 3),
410 MUXFLAG_READ = (1 << 6),
411 MUXFLAG_WRITE = (1 << 7),
413 MUXFLAG_UID = (1 << 9),
415 MUXFLAG_BEEP = (1 << 11),
417 MUXFLAG_BEEP_STOP = (1 << 13),
419 MUXFLAG_NO_RWSYNC = (1 << 15),
423};
424
436 OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
437};
438
457});
458
460 unsigned int destruction_ok;
463
464 /* The filestream is held in the datastore so it can be stopped
465 * immediately during stop_mixmonitor or channel destruction. */
467
471
473
474 unsigned int samp_rate;
475 char *filename;
476 char *beep_id;
477};
478
479/*!
480 * \internal
481 * \pre mixmonitor_ds must be locked before calling this function
482 */
484{
485 unsigned char quitting = 0;
486
487 if (mixmonitor_ds->fs) {
488 quitting = 1;
491 ast_verb(2, "MixMonitor close filestream (mixed)\n");
492 }
493
494 if (mixmonitor_ds->fs_read) {
495 quitting = 1;
498 ast_verb(2, "MixMonitor close filestream (read)\n");
499 }
500
501 if (mixmonitor_ds->fs_write) {
502 quitting = 1;
505 ast_verb(2, "MixMonitor close filestream (write)\n");
506 }
507
508 if (quitting) {
510 }
511}
512
513static void mixmonitor_ds_destroy(void *data)
514{
515 struct mixmonitor_ds *mixmonitor_ds = data;
516
524}
525
527 .type = "mixmonitor",
528 .destroy = mixmonitor_ds_destroy,
529};
530
532{
537 }
538 /* kill the audiohook.*/
543}
544
545static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
546{
547 if (!chan) {
548 return -1;
549 }
550
551 return ast_audiohook_attach(chan, audiohook);
552}
553
554/*!
555 * \internal
556 * \brief adds recipients to a mixmonitor's recipient list
557 * \param mixmonitor mixmonitor being affected
558 * \param vm_recipients string containing the desired recipients to add
559 */
560static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
561{
562 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
563 char *cur_mailbox = ast_strdupa(vm_recipients);
564 char *cur_context;
565 char *cur_folder;
566 char *next;
567 int elements_processed = 0;
568
569 while (!ast_strlen_zero(cur_mailbox)) {
570 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
571 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
572 *(next++) = '\0';
573 }
574
575 if ((cur_folder = strchr(cur_mailbox, '/'))) {
576 *(cur_folder++) = '\0';
577 } else {
578 cur_folder = "INBOX";
579 }
580
581 if ((cur_context = strchr(cur_mailbox, '@'))) {
582 *(cur_context++) = '\0';
583 } else {
584 cur_context = "default";
585 }
586
587 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
588 struct vm_recipient *recipient;
589 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
590 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
591 return;
592 }
593 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
594 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
595 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
596
597 /* Add to list */
598 ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
600 } else {
601 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
602 }
603
604 cur_mailbox = next;
605 elements_processed++;
606 }
607}
608
610{
611 struct vm_recipient *current;
613 /* Clear list element data */
615 }
616}
617
618#define SAMPLES_PER_FRAME 160
619
621{
622 if (mixmonitor) {
627 }
628
634
635 /* Free everything in the recipient list */
637
638 /* clean stringfields */
640
642 }
643}
644
645/*!
646 * \internal
647 * \brief Copies the mixmonitor to all voicemail recipients
648 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
649 * \param ext Format of the file that was saved
650 * \param filename
651 */
652static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
653{
654 struct vm_recipient *recipient = NULL;
655 struct ast_vm_recording_data recording_data;
656 if (ast_string_field_init(&recording_data, 512)) {
657 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
658 return;
659 }
660
661 /* Copy strings to stringfields that will be used for all recipients */
662 ast_string_field_set(&recording_data, recording_file, filename);
663 ast_string_field_set(&recording_data, recording_ext, ext);
668 /* and call_priority gets copied too */
669 recording_data.call_priority = mixmonitor->call_priority;
670
671 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
672 /* context, mailbox, and folder need to be set per recipient */
673 ast_string_field_set(&recording_data, context, recipient->context);
674 ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
675 ast_string_field_set(&recording_data, folder, recipient->folder);
676
677 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
678 recording_data.context);
679 ast_app_copy_recording_to_vm(&recording_data);
680 }
681
682 /* Free the string fields for recording_data before exiting the function. */
683 ast_string_field_free_memory(&recording_data);
684}
685
686static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
687{
688 /* Initialize the file if not already done so */
689 char *last_slash = NULL;
690 if (!ast_strlen_zero(filename)) {
691 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
692 *oflags = O_CREAT | O_WRONLY;
693 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
694
695 last_slash = strrchr(filename, '/');
696
697 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
698 **ext = '\0';
699 *ext = *ext + 1;
700 } else {
701 *ext = "raw";
702 }
703
704 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
705 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
706 *errflag = 1;
707 } else {
708 struct ast_filestream *tmp = *fs;
710 }
711 }
712 }
713}
714
716{
717 int is_bridged;
718
720 is_bridged = ast_channel_is_bridged(autochan->chan);
722 return is_bridged;
723}
724
725static void *mixmonitor_thread(void *obj)
726{
727 struct mixmonitor *mixmonitor = obj;
728 char *fs_ext = "";
729 char *fs_read_ext = "";
730 char *fs_write_ext = "";
731
732 struct ast_filestream **fs = NULL;
733 struct ast_filestream **fs_read = NULL;
734 struct ast_filestream **fs_write = NULL;
735
736 unsigned int oflags;
737 int errflag = 0;
738 struct ast_format *format_slin;
739
740 /* Keep callid association before any log messages */
741 if (mixmonitor->callid) {
743 }
744
745 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
746
748 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
749 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
750
752 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
753 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
754 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
755
757
759
760 /* The audiohook must enter and exit the loop locked */
763 struct ast_frame *fr = NULL;
764 struct ast_frame *fr_read = NULL;
765 struct ast_frame *fr_write = NULL;
766
768 &fr_read, &fr_write))) {
770
772 break;
773 }
774 continue;
775 }
776
777 /* audiohook lock is not required for the next block.
778 * Unlock it, but remember to lock it before looping or exiting */
780
784
785 /* Write out the frame(s) */
786 if ((*fs_read) && (fr_read)) {
787 struct ast_frame *cur;
788
789 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
790 ast_writestream(*fs_read, cur);
791 }
792 }
793
794 if ((*fs_write) && (fr_write)) {
795 struct ast_frame *cur;
796
797 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
798 ast_writestream(*fs_write, cur);
799 }
800 }
801
803 /* The 'D' option is set, so mix the frame as an interleaved dual channel frame */
804 int i;
805 short read_buf[SAMPLES_PER_FRAME];
807 short stereo_buf[SAMPLES_PER_FRAME * 2];
808 struct ast_frame stereo_frame = {
810 .datalen = sizeof(stereo_buf),
812 };
813
814 if (fr) {
815 ast_frame_free(fr, 0);
816 fr = NULL;
817 }
818
819 if (fr_read) {
820 memcpy(read_buf, fr_read->data.ptr, sizeof(read_buf));
821 } else {
822 memset(read_buf, 0, sizeof(read_buf));
823 }
824
825 if (fr_write) {
826 memcpy(write_buf, fr_write->data.ptr, sizeof(write_buf));
827 } else {
828 memset(write_buf, 0, sizeof(write_buf));
829 }
830
831 for (i = 0; i < SAMPLES_PER_FRAME; i++) {
832 stereo_buf[i * 2] = read_buf[i];
833 stereo_buf[i * 2 + 1] = write_buf[i];
834 }
835
836 stereo_frame.data.ptr = stereo_buf;
838
839 fr = ast_frdup(&stereo_frame);
840 }
841
842 if ((*fs) && (fr)) {
843 struct ast_frame *cur;
844
845 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
846 ast_writestream(*fs, cur);
847 }
848 }
850 }
851 /* All done! free it. */
852 if (fr) {
853 ast_frame_free(fr, 0);
854 }
855 if (fr_read) {
856 ast_frame_free(fr_read, 0);
857 }
858 if (fr_write) {
859 ast_frame_free(fr_write, 0);
860 }
861
862 fr = NULL;
863 fr_write = NULL;
864 fr_read = NULL;
865
867 }
868
870
875 }
876
878
879 /* Datastore cleanup. close the filestream and wait for ds destruction */
884 }
886
887 /* kill the audiohook */
889
891 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
893 }
894
895 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
896 ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
897
899 if (ast_strlen_zero(fs_ext)) {
900 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
901 mixmonitor -> name);
902 } else {
903 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
905 }
906 if (!ast_strlen_zero(fs_read_ext)) {
907 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
909 }
910 if (!ast_strlen_zero(fs_write_ext)) {
911 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
913 }
914 } else {
915 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
916 }
917
919 ast_debug(3, "Deleting our copies of recording files\n");
920 if (!ast_strlen_zero(fs_ext)) {
922 }
923 if (!ast_strlen_zero(fs_read_ext)) {
925 }
926 if (!ast_strlen_zero(fs_write_ext)) {
928 }
929 }
930
932
934 return NULL;
935}
936
937static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
938{
939 struct ast_datastore *datastore = NULL;
941
942 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
943 return -1;
944 }
945
946 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
947 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
949 return -1;
950 }
951
954
955 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
959 return -1;
960 }
961
966 }
967
968 mixmonitor_ds->samp_rate = 8000;
971 if (!ast_strlen_zero(beep_id)) {
973 }
974 datastore->data = mixmonitor_ds;
975
976 ast_channel_lock(chan);
977 ast_channel_datastore_add(chan, datastore);
978 ast_channel_unlock(chan);
979
981 return 0;
982}
983
984static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
985{
986 struct ast_datastore *datastore;
987
988 ast_channel_lock(chan);
989
990 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
991
992 /*
993 * Currently the one place this function is called from guarantees a
994 * datastore is present, thus return checks can be avoided here.
995 */
996 ast_channel_datastore_remove(chan, datastore);
997 ast_datastore_free(datastore);
998
999 ast_channel_unlock(chan);
1000}
1001
1002static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
1003 unsigned int flags, int readvol, int writevol,
1004 const char *post_process, const char *filename_write,
1005 char *filename_read, const char *uid_channel_var,
1006 const char *recipients, const char *beep_id)
1007{
1008 pthread_t thread;
1009 struct mixmonitor *mixmonitor;
1010 char postprocess2[1024] = "";
1011 char *datastore_id = NULL;
1012
1013 postprocess2[0] = 0;
1014 /* If a post process system command is given attach it to the structure */
1016 char *p1, *p2;
1017
1019 for (p2 = p1; *p2; p2++) {
1020 if (*p2 == '^' && *(p2+1) == '{') {
1021 *p2 = '$';
1022 }
1023 }
1024 ast_channel_lock(chan);
1025 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
1026 ast_channel_unlock(chan);
1027 }
1028
1029 /* Pre-allocate mixmonitor structure and spy */
1030 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
1031 return -1;
1032 }
1033
1034 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
1037 return -1;
1038 }
1039
1040 /* Setup the actual spy before creating our thread */
1043 return -1;
1044 }
1045
1046 /* Copy over flags and channel name */
1048 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1050 return -1;
1051 }
1052
1053 if (!ast_strlen_zero(filename)) {
1055 }
1056
1059 }
1060
1063 }
1064
1065 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1068 ast_free(datastore_id);
1069 return -1;
1070 }
1071
1072 if (!ast_strlen_zero(uid_channel_var)) {
1073 if (datastore_id) {
1074 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1075 }
1076 }
1077
1079
1080 if (!ast_strlen_zero(postprocess2)) {
1081 mixmonitor->post_process = ast_strdup(postprocess2);
1082 }
1083
1084 if (!ast_strlen_zero(recipients)) {
1085 char callerid[256];
1086
1087 ast_channel_lock(chan);
1088
1089 /* We use the connected line of the invoking channel for caller ID,
1090 * unless we've been told to use the Caller ID.
1091 * The initial use for this relied on Connected Line to get the
1092 * actual number for recording with Digium phones,
1093 * but in generic use the Caller ID is likely what people want.
1094 */
1095
1097 struct ast_party_caller *caller;
1098 caller = ast_channel_caller(chan);
1099 ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1100 caller->id.name.str, caller->id.number.valid,
1101 caller->id.number.str);
1102 ast_callerid_merge(callerid, sizeof(callerid),
1103 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1104 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1105 "Unknown");
1106 } else {
1109 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1110 connected->id.name.str, connected->id.number.valid,
1111 connected->id.number.str);
1112 ast_callerid_merge(callerid, sizeof(callerid),
1113 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1114 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1115 "Unknown");
1116 }
1117
1119 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1120 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1121 ast_string_field_set(mixmonitor, call_callerid, callerid);
1123
1124 ast_channel_unlock(chan);
1125
1127 }
1128
1132 }
1133
1134 if (readvol)
1136 if (writevol)
1138
1139 if (startmon(chan, &mixmonitor->audiohook)) {
1140 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1142 mixmonitor_ds_remove_and_free(chan, datastore_id);
1143 ast_free(datastore_id);
1147 return -1;
1148 }
1149
1150 ast_free(datastore_id);
1151
1152 /* reference be released at mixmonitor destruction */
1154
1156}
1157
1158/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1159/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1160static char *filename_parse(char *filename, char *buffer, size_t len)
1161{
1162 char *slash;
1163 char *ext;
1164
1165 ast_assert(len > 0);
1166
1167 if (ast_strlen_zero(filename)) {
1168 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1169 buffer[0] = 0;
1170 return buffer;
1171 }
1172
1173 /* If we don't have an absolute path, make one */
1174 if (*filename != '/') {
1175 char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1176 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1177 filename = build;
1178 }
1179
1180 ast_copy_string(buffer, filename, len);
1181
1182 /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1183 match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1184 ends up referring to a file that does not/will not exist */
1185 ext = strrchr(buffer, '.');
1186 if (ext && !strcmp(ext, ".wav49")) {
1187 /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1188 * so this is safe */
1189 memcpy(ext, ".WAV", sizeof(".WAV"));
1190 }
1191
1192 if ((slash = strrchr(filename, '/'))) {
1193 *slash = '\0';
1194 }
1195 ast_mkdir(filename, 0777);
1196
1197 return buffer;
1198}
1199
1200static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1201{
1202 int x, readvol = 0, writevol = 0;
1203 char *filename_read = NULL;
1204 char *filename_write = NULL;
1205 char filename_buffer[1024] = "";
1206 char *uid_channel_var = NULL;
1207 char beep_id[64] = "";
1208
1209 struct ast_flags flags = { 0 };
1210 char *recipients = NULL;
1211 char *parse;
1214 AST_APP_ARG(filename);
1216 AST_APP_ARG(post_process);
1217 );
1218
1219 if (ast_strlen_zero(data)) {
1220 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1221 return -1;
1222 }
1223
1224 parse = ast_strdupa(data);
1225
1227
1228 if (args.options) {
1229 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1230
1232
1234 ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1235 " and does not need to be specified.\n");
1236 }
1237
1240 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1241 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1242 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1243 } else {
1244 readvol = get_volfactor(x);
1245 }
1246 }
1247
1250 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1251 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1252 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1253 } else {
1254 writevol = get_volfactor(x);
1255 }
1256 }
1257
1259 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1260 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1261 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1262 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1263 } else {
1264 readvol = writevol = get_volfactor(x);
1265 }
1266 }
1267
1270 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1271 } else {
1272 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1273 }
1274 }
1275
1277 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1278 }
1279
1281 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1282 }
1283
1285 uid_channel_var = opts[OPT_ARG_UID];
1286 }
1287
1289 const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1290 unsigned int interval = 15;
1291
1292 if (sscanf(interval_str, "%30u", &interval) != 1) {
1293 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1294 interval_str, interval);
1295 }
1296
1297 if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1298 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1299 return -1;
1300 }
1301 }
1302 }
1303 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1304
1306 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1307 return -1;
1308 }
1309
1310 /* If filename exists, try to create directories for it */
1311 if (!(ast_strlen_zero(args.filename))) {
1312 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1313 }
1314
1315 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1316
1317 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1319 if (launch_monitor_thread(chan,
1320 args.filename,
1321 flags.flags,
1322 readvol,
1323 writevol,
1324 args.post_process,
1325 filename_write,
1326 filename_read,
1327 uid_channel_var,
1328 recipients,
1329 beep_id)) {
1331 }
1332
1335 if (message) {
1337 }
1338
1339 return 0;
1340}
1341
1342static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1343{
1344 struct ast_datastore *datastore = NULL;
1345 char *parse = "";
1347 const char *beep_id = NULL;
1349
1351 AST_APP_ARG(mixmonid);
1352 );
1353
1354 if (!ast_strlen_zero(data)) {
1355 parse = ast_strdupa(data);
1356 }
1357
1359
1360 ast_channel_lock(chan);
1361
1363 S_OR(args.mixmonid, NULL));
1364 if (!datastore) {
1365 ast_channel_unlock(chan);
1366 return -1;
1367 }
1368 mixmonitor_ds = datastore->data;
1369
1371
1372 /* closing the filestream here guarantees the file is available to the dialplan
1373 * after calling StopMixMonitor */
1375
1376 /* The mixmonitor thread may be waiting on the audiohook trigger.
1377 * In order to exit from the mixmonitor loop before waiting on channel
1378 * destruction, poke the audiohook trigger. */
1379 if (mixmonitor_ds->audiohook) {
1382 }
1387 }
1388
1391 }
1392
1394
1395 /* Remove the datastore so the monitor thread can exit */
1396 if (!ast_channel_datastore_remove(chan, datastore)) {
1397 ast_datastore_free(datastore);
1398 }
1399
1400 ast_channel_unlock(chan);
1401
1402 if (!ast_strlen_zero(beep_id)) {
1403 ast_beep_stop(chan, beep_id);
1404 }
1405
1408 NULL);
1409 if (message) {
1411 }
1412
1413 return 0;
1414}
1415
1416static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1417{
1418 stop_mixmonitor_full(chan, data);
1419 return 0;
1420}
1421
1422static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1423{
1424 struct ast_channel *chan;
1425 struct ast_datastore *datastore = NULL;
1427
1428 switch (cmd) {
1429 case CLI_INIT:
1430 e->command = "mixmonitor {start|stop|list}";
1431 e->usage =
1432 "Usage: mixmonitor start <chan_name> [args]\n"
1433 " The optional arguments are passed to the MixMonitor application.\n"
1434 " mixmonitor stop <chan_name> [args]\n"
1435 " The optional arguments are passed to the StopMixMonitor application.\n"
1436 " mixmonitor list <chan_name>\n";
1437 return NULL;
1438 case CLI_GENERATE:
1439 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1440 }
1441
1442 if (a->argc < 3) {
1443 return CLI_SHOWUSAGE;
1444 }
1445
1446 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1447 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1448 /* Technically this is a failure, but we don't want 2 errors printing out */
1449 return CLI_SUCCESS;
1450 }
1451
1452 if (!strcasecmp(a->argv[1], "start")) {
1453 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1454 } else if (!strcasecmp(a->argv[1], "stop")){
1455 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1456 } else if (!strcasecmp(a->argv[1], "list")) {
1457 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1458 ast_cli(a->fd, "=========================================================================\n");
1459 ast_channel_lock(chan);
1461 if (datastore->info == &mixmonitor_ds_info) {
1462 char *filename = "";
1463 char *filename_read = "";
1464 char *filename_write = "";
1465
1466 mixmonitor_ds = datastore->data;
1467 if (mixmonitor_ds->fs) {
1469 }
1470 if (mixmonitor_ds->fs_read) {
1471 filename_read = mixmonitor_ds->fs_read->filename;
1472 }
1473 if (mixmonitor_ds->fs_write) {
1474 filename_write = mixmonitor_ds->fs_write->filename;
1475 }
1476 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1477 }
1478 }
1479 ast_channel_unlock(chan);
1480 } else {
1481 chan = ast_channel_unref(chan);
1482 return CLI_SHOWUSAGE;
1483 }
1484
1485 chan = ast_channel_unref(chan);
1486
1487 return CLI_SUCCESS;
1488}
1489
1490/*! \brief Mute / unmute an individual MixMonitor by id */
1491static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1492 enum ast_audiohook_flags flag, int clearmute)
1493{
1494 struct ast_datastore *datastore = NULL;
1495 char *parse = "";
1497
1499 AST_APP_ARG(mixmonid);
1500 );
1501
1502 if (!ast_strlen_zero(data)) {
1503 parse = ast_strdupa(data);
1504 }
1505
1507
1508 ast_channel_lock(chan);
1509
1511 S_OR(args.mixmonid, NULL));
1512 if (!datastore) {
1513 ast_channel_unlock(chan);
1514 return -1;
1515 }
1516 mixmonitor_ds = datastore->data;
1517
1519
1520 if (mixmonitor_ds->audiohook) {
1521 if (clearmute) {
1523 } else {
1525 }
1526 }
1527
1529 ast_channel_unlock(chan);
1530
1531 return 0;
1532}
1533
1534/*! \brief Mute / unmute a MixMonitor channel */
1535static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1536{
1537 struct ast_channel *c;
1538 const char *name = astman_get_header(m, "Channel");
1539 const char *id = astman_get_header(m, "ActionID");
1540 const char *state = astman_get_header(m, "State");
1541 const char *direction = astman_get_header(m,"Direction");
1542 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1543 int clearmute = 1, mutedcount = 0;
1546 RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1547
1549 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1550 return AMI_SUCCESS;
1551 }
1552
1553 if (!strcasecmp(direction, "read")) {
1555 } else if (!strcasecmp(direction, "write")) {
1557 } else if (!strcasecmp(direction, "both")) {
1559 } else {
1560 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1561 return AMI_SUCCESS;
1562 }
1563
1564 if (ast_strlen_zero(name)) {
1565 astman_send_error(s, m, "No channel specified");
1566 return AMI_SUCCESS;
1567 }
1568
1569 if (ast_strlen_zero(state)) {
1570 astman_send_error(s, m, "No state specified");
1571 return AMI_SUCCESS;
1572 }
1573
1574 clearmute = ast_false(state);
1575
1577 if (!c) {
1578 astman_send_error(s, m, "No such channel");
1579 return AMI_SUCCESS;
1580 }
1581
1582 if (ast_strlen_zero(mixmonitor_id)) {
1583 mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1584 if (mutedcount < 0) {
1586 astman_send_error(s, m, "Cannot set mute flag");
1587 return AMI_SUCCESS;
1588 }
1589 } else {
1590 if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1592 astman_send_error(s, m, "Cannot set mute flag");
1593 return AMI_SUCCESS;
1594 }
1595 mutedcount = 1;
1596 }
1597
1598
1599 stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1600 "direction", direction,
1601 "state", ast_true(state),
1602 "mixmonitorid", mixmonitor_id,
1603 "count", mutedcount);
1604
1606 ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1607
1608 if (stasis_message) {
1610 }
1611
1612 astman_append(s, "Response: Success\r\n");
1613
1614 if (!ast_strlen_zero(id)) {
1615 astman_append(s, "ActionID: %s\r\n", id);
1616 }
1617
1618 astman_append(s, "\r\n");
1619
1621
1622 return AMI_SUCCESS;
1623}
1624
1625static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1626{
1627 char args[PATH_MAX];
1628
1629 if (ast_strlen_zero(options)) {
1630 snprintf(args, sizeof(args), "%s", filename);
1631 } else {
1632 snprintf(args, sizeof(args), "%s,%s", filename, options);
1633 }
1634
1635 return mixmonitor_exec(chan, args);
1636}
1637
1638static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1639{
1640 return stop_mixmonitor_full(chan, mixmonitor_id);
1641}
1642
1643static int manager_mixmonitor(struct mansession *s, const struct message *m)
1644{
1645 struct ast_channel *c;
1646 const char *name = astman_get_header(m, "Channel");
1647 const char *id = astman_get_header(m, "ActionID");
1648 const char *file = astman_get_header(m, "File");
1649 const char *options = astman_get_header(m, "Options");
1650 const char *command = astman_get_header(m, "Command");
1651 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1652 struct ast_flags flags = { 0 };
1653 char *uid_channel_var = NULL;
1654 const char *mixmonitor_id = NULL;
1655 int res;
1656 char args[PATH_MAX];
1657
1658 if (ast_strlen_zero(name)) {
1659 astman_send_error(s, m, "No channel specified");
1660 return AMI_SUCCESS;
1661 }
1662
1664 if (!c) {
1665 astman_send_error(s, m, "No such channel");
1666 return AMI_SUCCESS;
1667 }
1668
1669 if (!ast_strlen_zero(options)) {
1671 }
1672
1673 snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1674
1675 res = mixmonitor_exec(c, args);
1676
1678 uid_channel_var = opts[OPT_ARG_UID];
1680 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1681 mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1683 }
1684
1685 if (res) {
1687 astman_send_error(s, m, "Could not start monitoring channel");
1688 return AMI_SUCCESS;
1689 }
1690
1691 astman_append(s, "Response: Success\r\n");
1692
1693 if (!ast_strlen_zero(id)) {
1694 astman_append(s, "ActionID: %s\r\n", id);
1695 }
1696
1697 if (!ast_strlen_zero(mixmonitor_id)) {
1698 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1699 }
1700
1701 astman_append(s, "\r\n");
1702
1704
1705 return AMI_SUCCESS;
1706}
1707
1708static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1709{
1710 struct ast_channel *c;
1711 const char *name = astman_get_header(m, "Channel");
1712 const char *id = astman_get_header(m, "ActionID");
1713 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1714 int res;
1715
1716 if (ast_strlen_zero(name)) {
1717 astman_send_error(s, m, "No channel specified");
1718 return AMI_SUCCESS;
1719 }
1720
1722 if (!c) {
1723 astman_send_error(s, m, "No such channel");
1724 return AMI_SUCCESS;
1725 }
1726
1727 res = stop_mixmonitor_full(c, mixmonitor_id);
1728 if (res) {
1730 astman_send_error(s, m, "Could not stop monitoring channel");
1731 return AMI_SUCCESS;
1732 }
1733
1734 astman_append(s, "Response: Success\r\n");
1735
1736 if (!ast_strlen_zero(id)) {
1737 astman_append(s, "ActionID: %s\r\n", id);
1738 }
1739
1740 astman_append(s, "\r\n");
1741
1743
1744 return AMI_SUCCESS;
1745}
1746
1747static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1748 char *buf, size_t len)
1749{
1750 struct ast_datastore *datastore;
1751 struct mixmonitor_ds *ds_data;
1753 AST_APP_ARG(id);
1754 AST_APP_ARG(key);
1755 );
1756
1758
1759 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1760 ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1761 "An ID and key must be provided\n", cmd);
1762 return -1;
1763 }
1764
1765 ast_channel_lock(chan);
1766 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1767 ast_channel_unlock(chan);
1768
1769 if (!datastore) {
1770 ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1771 return -1;
1772 }
1773
1774 ds_data = datastore->data;
1775
1776 if (!strcasecmp(args.key, "filename")) {
1777 ast_copy_string(buf, ds_data->filename, len);
1778 } else {
1779 ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
1780 return -1;
1781 }
1782 return 0;
1783}
1784
1786 .name = "MIXMONITOR",
1787 .read = func_mixmonitor_read,
1788};
1789
1791 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1792};
1793
1795{
1799 };
1800
1802}
1803
1805{
1807}
1808
1809static int unload_module(void)
1810{
1811 int res;
1812
1816 res |= ast_manager_unregister("MixMonitorMute");
1817 res |= ast_manager_unregister("MixMonitor");
1818 res |= ast_manager_unregister("StopMixMonitor");
1820 res |= clear_mixmonitor_methods();
1821
1822 return res;
1823}
1824
1825static int load_module(void)
1826{
1827 int res;
1828
1836 res |= set_mixmonitor_methods();
1837
1838 return res;
1839}
1840
1841AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
1842 .support_level = AST_MODULE_SUPPORT_CORE,
1843 .load = load_module,
1844 .unload = unload_module,
1845 .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:329
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:451
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:1094
void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
Update audiohook's status.
Definition: audiohook.c:540
#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:1380
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:550
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:484
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.
static int tmp()
Definition: bt_open.c:389
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:2404
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2413
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:2968
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:1453
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition: channel.c:10567
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3004
#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:1473
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:2969
#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:2418
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:1423
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1878
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
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:1969
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1630
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1890
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7606
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:2298
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition: logger.c:2320
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:202
#define ast_cond_wait(cond, mutex)
Definition: lock.h:205
#define ast_cond_init(cond, attr)
Definition: lock.h:201
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
pthread_cond_t ast_cond_t
Definition: lock.h:178
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
#define ast_cond_signal(cond)
Definition: lock.h:203
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:191
#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:1558
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:1512
#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
char * filename
Definition: mod_format.h:107
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
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
union ast_frame::@226 data
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:135
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
Definition: search.h:40
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:326
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