Asterisk - The Open Source Telephony Project GIT-master-77d630f
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 </optionlist>
162 </parameter>
163 <parameter name="command">
164 <para>Will be executed when the recording is over.</para>
165 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
166 <para>All variables will be evaluated at the time MixMonitor is called.</para>
167 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
168 or <variable>CALLERID(name)</variable> as part of the command parameters. You
169 risk a command injection attack executing arbitrary commands if the untrusted
170 strings aren't filtered to remove dangerous characters. See function
171 <variable>FILTER()</variable>.</para></warning>
172 </parameter>
173 </syntax>
174 <description>
175 <para>Records the audio on the current channel to the specified file.</para>
176 <para>This application does not automatically answer and should be preceded by
177 an application such as Answer or Progress().</para>
178 <note><para>MixMonitor runs as an audiohook.</para></note>
179 <note><para>If a filename passed to MixMonitor ends with
180 <literal>.wav49</literal>, Asterisk will silently convert the extension to
181 <literal>.WAV</literal> for legacy reasons. <variable>MIXMONITOR_FILENAME</variable>
182 will contain the actual filename that Asterisk is writing to, not necessarily the
183 value that was passed in.</para></note>
184 <variablelist>
185 <variable name="MIXMONITOR_FILENAME">
186 <para>Will contain the filename used to record.</para>
187 </variable>
188 </variablelist>
189 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
190 or <variable>CALLERID(name)</variable> as part of ANY of the application's
191 parameters. You risk a command injection attack executing arbitrary commands
192 if the untrusted strings aren't filtered to remove dangerous characters. See
193 function <variable>FILTER()</variable>.</para></warning>
194 <warning><para>When using the <literal>D</literal> option to save
195 interleaved audio, you MUST use <literal>.raw</literal> as the
196 file extension. Any other extension will produce a corrupted file.</para></warning>
197 </description>
198 <see-also>
199 <ref type="application">StopMixMonitor</ref>
200 </see-also>
201 </application>
202 <application name="StopMixMonitor" language="en_US">
203 <since>
204 <version>1.4.0</version>
205 </since>
206 <synopsis>
207 Stop recording a call through MixMonitor, and free the recording's file handle.
208 </synopsis>
209 <syntax>
210 <parameter name="MixMonitorID" required="false">
211 <para>If a valid ID is provided, then this command will stop only that specific
212 MixMonitor.</para>
213 </parameter>
214 </syntax>
215 <description>
216 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
217 on the current channel.</para>
218 </description>
219 <see-also>
220 <ref type="application">MixMonitor</ref>
221 </see-also>
222 </application>
223 <manager name="MixMonitorMute" language="en_US">
224 <since>
225 <version>1.8.0</version>
226 </since>
227 <synopsis>
228 Mute / unMute a Mixmonitor recording.
229 </synopsis>
230 <syntax>
231 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
232 <parameter name="Channel" required="true">
233 <para>Used to specify the channel to mute.</para>
234 </parameter>
235 <parameter name="Direction">
236 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
237 </parameter>
238 <parameter name="State">
239 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
240 </parameter>
241 </syntax>
242 <description>
243 <para>This action may be used to mute a MixMonitor recording.</para>
244 </description>
245 </manager>
246 <manager name="MixMonitor" language="en_US">
247 <since>
248 <version>11.0.0</version>
249 </since>
250 <synopsis>
251 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
252 to guarantee the audio file is available for processing during dialplan execution.
253 </synopsis>
254 <syntax>
255 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
256 <parameter name="Channel" required="true">
257 <para>Used to specify the channel to record.</para>
258 </parameter>
259 <parameter name="File">
260 <para>Is the name of the file created in the monitor spool directory.
261 Defaults to the same name as the channel (with slashes replaced with dashes).
262 This argument is optional if you specify to record unidirectional audio with
263 either the r(filename) or t(filename) options in the options field. If
264 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
265 be recorded.</para>
266 </parameter>
267 <parameter name="Options">
268 <para>Options that apply to the MixMonitor in the same way as they
269 would apply if invoked from the MixMonitor application. For a list of
270 available options, see the documentation for the mixmonitor application. </para>
271 </parameter>
272 <parameter name="Command">
273 <para>Will be executed when the recording is over.
274 Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
275 All variables will be evaluated at the time MixMonitor is called.</para>
276 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
277 or <variable>CALLERID(name)</variable> as part of the command parameters. You
278 risk a command injection attack executing arbitrary commands if the untrusted
279 strings aren't filtered to remove dangerous characters. See function
280 <variable>FILTER()</variable>.</para></warning>
281 </parameter>
282 </syntax>
283 <description>
284 <para>This action records the audio on the current channel to the specified file.</para>
285 <variablelist>
286 <variable name="MIXMONITOR_FILENAME">
287 <para>Will contain the filename used to record the mixed stream.</para>
288 </variable>
289 </variablelist>
290 </description>
291 </manager>
292 <manager name="StopMixMonitor" language="en_US">
293 <since>
294 <version>11.0.0</version>
295 </since>
296 <synopsis>
297 Stop recording a call through MixMonitor, and free the recording's file handle.
298 </synopsis>
299 <syntax>
300 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
301 <parameter name="Channel" required="true">
302 <para>The name of the channel monitored.</para>
303 </parameter>
304 <parameter name="MixMonitorID" required="false">
305 <para>If a valid ID is provided, then this command will stop only that specific
306 MixMonitor.</para>
307 </parameter>
308 </syntax>
309 <description>
310 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
311 action on the current channel.</para>
312 </description>
313 </manager>
314 <function name="MIXMONITOR" language="en_US">
315 <since>
316 <version>13.0.0</version>
317 </since>
318 <synopsis>
319 Retrieve data pertaining to specific instances of MixMonitor on a channel.
320 </synopsis>
321 <syntax>
322 <parameter name="id" required="true">
323 <para>The unique ID of the MixMonitor instance. The unique ID can be retrieved through the channel
324 variable used as an argument to the <replaceable>i</replaceable> option to MixMonitor.</para>
325 </parameter>
326 <parameter name="key" required="true">
327 <para>The piece of data to retrieve from the MixMonitor.</para>
328 <enumlist>
329 <enum name="filename" />
330 </enumlist>
331 </parameter>
332 </syntax>
333 </function>
334 <managerEvent language="en_US" name="MixMonitorStart">
335 <managerEventInstance class="EVENT_FLAG_CALL">
336 <since>
337 <version>16.17.0</version>
338 <version>18.3.0</version>
339 </since>
340 <synopsis>Raised when monitoring has started on a channel.</synopsis>
341 <syntax>
342 <channel_snapshot/>
343 </syntax>
344 <see-also>
345 <ref type="managerEvent">MixMonitorStop</ref>
346 <ref type="application">MixMonitor</ref>
347 <ref type="manager">MixMonitor</ref>
348 </see-also>
349 </managerEventInstance>
350 </managerEvent>
351 <managerEvent language="en_US" name="MixMonitorStop">
352 <managerEventInstance class="EVENT_FLAG_CALL">
353 <since>
354 <version>16.17.0</version>
355 <version>18.3.0</version>
356 </since>
357 <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
358 <syntax>
359 <channel_snapshot/>
360 </syntax>
361 <see-also>
362 <ref type="managerEvent">MixMonitorStart</ref>
363 <ref type="application">StopMixMonitor</ref>
364 <ref type="manager">StopMixMonitor</ref>
365 </see-also>
366 </managerEventInstance>
367 </managerEvent>
368 <managerEvent language="en_US" name="MixMonitorMute">
369 <managerEventInstance class="EVENT_FLAG_CALL">
370 <since>
371 <version>16.17.0</version>
372 <version>18.3.0</version>
373 </since>
374 <synopsis>Raised when monitoring is muted or unmuted on a channel.</synopsis>
375 <syntax>
376 <channel_snapshot/>
377 <parameter name="Direction">
378 <para>Which part of the recording was muted or unmuted: read, write or both
379 (from channel, to channel or both directions).</para>
380 </parameter>
381 <parameter name="State">
382 <para>If the monitoring was muted or unmuted: 1 when muted, 0 when unmuted.</para>
383 </parameter>
384 </syntax>
385 <see-also>
386 <ref type="manager">MixMonitorMute</ref>
387 </see-also>
388 </managerEventInstance>
389 </managerEvent>
390
391
392 ***/
393
394#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
395
396static const char * const app = "MixMonitor";
397
398static const char * const stop_app = "StopMixMonitor";
399
400static const char * const mixmonitor_spy_type = "MixMonitor";
401
402/*!
403 * \internal
404 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
405 */
409 char folder[80];
411};
412
415 char *filename;
419 char *name;
421 unsigned int flags;
424
425 /* the below string fields describe data used for creating voicemails from the recording */
431 );
433
434 /* FUTURE DEVELOPMENT NOTICE
435 * recipient_list will need locks if we make it editable after the monitor is started */
437};
438
440 MUXFLAG_APPEND = (1 << 1),
441 MUXFLAG_BRIDGED = (1 << 2),
442 MUXFLAG_VOLUME = (1 << 3),
445 MUXFLAG_READ = (1 << 6),
446 MUXFLAG_WRITE = (1 << 7),
448 MUXFLAG_UID = (1 << 9),
450 MUXFLAG_BEEP = (1 << 11),
452 MUXFLAG_BEEP_STOP = (1 << 13),
454 MUXFLAG_NO_RWSYNC = (1 << 15),
458};
459
471 OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
472};
473
492});
493
495 unsigned int destruction_ok;
498
499 /* The filestream is held in the datastore so it can be stopped
500 * immediately during stop_mixmonitor or channel destruction. */
502
506
508
509 unsigned int samp_rate;
510 char *filename;
511 char *beep_id;
512};
513
514/*!
515 * \internal
516 * \pre mixmonitor_ds must be locked before calling this function
517 */
519{
520 unsigned char quitting = 0;
521
522 if (mixmonitor_ds->fs) {
523 quitting = 1;
526 ast_verb(2, "MixMonitor close filestream (mixed)\n");
527 }
528
529 if (mixmonitor_ds->fs_read) {
530 quitting = 1;
533 ast_verb(2, "MixMonitor close filestream (read)\n");
534 }
535
536 if (mixmonitor_ds->fs_write) {
537 quitting = 1;
540 ast_verb(2, "MixMonitor close filestream (write)\n");
541 }
542
543 if (quitting) {
545 }
546}
547
548static void mixmonitor_ds_destroy(void *data)
549{
550 struct mixmonitor_ds *mixmonitor_ds = data;
551
559}
560
562 .type = "mixmonitor",
563 .destroy = mixmonitor_ds_destroy,
564};
565
567{
572 }
573 /* kill the audiohook.*/
578}
579
580static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
581{
582 if (!chan) {
583 return -1;
584 }
585
586 return ast_audiohook_attach(chan, audiohook);
587}
588
589/*!
590 * \internal
591 * \brief adds recipients to a mixmonitor's recipient list
592 * \param mixmonitor mixmonitor being affected
593 * \param vm_recipients string containing the desired recipients to add
594 */
595static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
596{
597 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
598 char *cur_mailbox = ast_strdupa(vm_recipients);
599 char *cur_context;
600 char *cur_folder;
601 char *next;
602 int elements_processed = 0;
603
604 while (!ast_strlen_zero(cur_mailbox)) {
605 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
606 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
607 *(next++) = '\0';
608 }
609
610 if ((cur_folder = strchr(cur_mailbox, '/'))) {
611 *(cur_folder++) = '\0';
612 } else {
613 cur_folder = "INBOX";
614 }
615
616 if ((cur_context = strchr(cur_mailbox, '@'))) {
617 *(cur_context++) = '\0';
618 } else {
619 cur_context = "default";
620 }
621
622 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
623 struct vm_recipient *recipient;
624 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
625 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
626 return;
627 }
628 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
629 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
630 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
631
632 /* Add to list */
633 ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
635 } else {
636 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
637 }
638
639 cur_mailbox = next;
640 elements_processed++;
641 }
642}
643
645{
646 struct vm_recipient *current;
648 /* Clear list element data */
650 }
651}
652
653#define SAMPLES_PER_FRAME 160
654
656{
657 if (mixmonitor) {
662 }
663
669
670 /* Free everything in the recipient list */
672
673 /* clean stringfields */
675
677 }
678}
679
680/*!
681 * \internal
682 * \brief Copies the mixmonitor to all voicemail recipients
683 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
684 * \param ext Format of the file that was saved
685 * \param filename
686 */
687static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
688{
689 struct vm_recipient *recipient = NULL;
692 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
693 return;
694 }
695
696 /* Copy strings to stringfields that will be used for all recipients */
703 /* and call_priority gets copied too */
704 recording_data.call_priority = mixmonitor->call_priority;
705
706 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
707 /* context, mailbox, and folder need to be set per recipient */
711
712 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
713 recording_data.context);
715 }
716
717 /* Free the string fields for recording_data before exiting the function. */
719}
720
721static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
722{
723 /* Initialize the file if not already done so */
724 char *last_slash = NULL;
725 if (!ast_strlen_zero(filename)) {
726 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
727 *oflags = O_CREAT | O_WRONLY;
728 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
729
730 last_slash = strrchr(filename, '/');
731
732 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
733 **ext = '\0';
734 *ext = *ext + 1;
735 } else {
736 *ext = "raw";
737 }
738
739 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
740 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
741 *errflag = 1;
742 } else {
743 struct ast_filestream *tmp = *fs;
745 }
746 }
747 }
748}
749
751{
752 int is_bridged;
753
755 is_bridged = ast_channel_is_bridged(autochan->chan);
757 return is_bridged;
758}
759
760static void *mixmonitor_thread(void *obj)
761{
762 struct mixmonitor *mixmonitor = obj;
763 char *fs_ext = "";
764 char *fs_read_ext = "";
765 char *fs_write_ext = "";
766
767 struct ast_filestream **fs = NULL;
768 struct ast_filestream **fs_read = NULL;
769 struct ast_filestream **fs_write = NULL;
770
771 unsigned int oflags;
772 int errflag = 0;
773 struct ast_format *format_slin;
774
775 /* Keep callid association before any log messages */
776 if (mixmonitor->callid) {
778 }
779
780 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
781
783 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
784 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
785
787 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
788 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
789 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
790
792
794
795 /* The audiohook must enter and exit the loop locked */
798 struct ast_frame *fr = NULL;
799 struct ast_frame *fr_read = NULL;
800 struct ast_frame *fr_write = NULL;
801
803 &fr_read, &fr_write))) {
805
807 break;
808 }
809 continue;
810 }
811
812 /* audiohook lock is not required for the next block.
813 * Unlock it, but remember to lock it before looping or exiting */
815
819
820 /* Write out the frame(s) */
821 if ((*fs_read) && (fr_read)) {
822 struct ast_frame *cur;
823
824 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
825 ast_writestream(*fs_read, cur);
826 }
827 }
828
829 if ((*fs_write) && (fr_write)) {
830 struct ast_frame *cur;
831
832 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
833 ast_writestream(*fs_write, cur);
834 }
835 }
836
838 /* The 'D' option is set, so mix the frame as an interleaved dual channel frame */
839 int i;
840 short read_buf[SAMPLES_PER_FRAME];
842 short stereo_buf[SAMPLES_PER_FRAME * 2];
843 struct ast_frame stereo_frame = {
845 .datalen = sizeof(stereo_buf),
847 };
848
849 if (fr) {
850 ast_frame_free(fr, 0);
851 fr = NULL;
852 }
853
854 if (fr_read) {
855 memcpy(read_buf, fr_read->data.ptr, sizeof(read_buf));
856 } else {
857 memset(read_buf, 0, sizeof(read_buf));
858 }
859
860 if (fr_write) {
861 memcpy(write_buf, fr_write->data.ptr, sizeof(write_buf));
862 } else {
863 memset(write_buf, 0, sizeof(write_buf));
864 }
865
866 for (i = 0; i < SAMPLES_PER_FRAME; i++) {
867 stereo_buf[i * 2] = read_buf[i];
868 stereo_buf[i * 2 + 1] = write_buf[i];
869 }
870
871 stereo_frame.data.ptr = stereo_buf;
873
874 fr = ast_frdup(&stereo_frame);
875 }
876
877 if ((*fs) && (fr)) {
878 struct ast_frame *cur;
879
880 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
881 ast_writestream(*fs, cur);
882 }
883 }
885 }
886 /* All done! free it. */
887 if (fr) {
888 ast_frame_free(fr, 0);
889 }
890 if (fr_read) {
891 ast_frame_free(fr_read, 0);
892 }
893 if (fr_write) {
894 ast_frame_free(fr_write, 0);
895 }
896
897 fr = NULL;
898 fr_write = NULL;
899 fr_read = NULL;
900
902 }
903
905
910 }
911
913
914 /* Datastore cleanup. close the filestream and wait for ds destruction */
919 }
921
922 /* kill the audiohook */
924
926 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
928 }
929
930 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
931 ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
932
934 if (ast_strlen_zero(fs_ext)) {
935 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
936 mixmonitor -> name);
937 } else {
938 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
940 }
941 if (!ast_strlen_zero(fs_read_ext)) {
942 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
944 }
945 if (!ast_strlen_zero(fs_write_ext)) {
946 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
948 }
949 } else {
950 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
951 }
952
954 ast_debug(3, "Deleting our copies of recording files\n");
955 if (!ast_strlen_zero(fs_ext)) {
957 }
958 if (!ast_strlen_zero(fs_read_ext)) {
960 }
961 if (!ast_strlen_zero(fs_write_ext)) {
963 }
964 }
965
967
969 return NULL;
970}
971
972static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
973{
974 struct ast_datastore *datastore = NULL;
976
977 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
978 return -1;
979 }
980
981 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
982 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
984 return -1;
985 }
986
989
990 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
994 return -1;
995 }
996
1001 }
1002
1003 mixmonitor_ds->samp_rate = 8000;
1006 if (!ast_strlen_zero(beep_id)) {
1008 }
1009 datastore->data = mixmonitor_ds;
1010
1011 ast_channel_lock(chan);
1012 ast_channel_datastore_add(chan, datastore);
1013 ast_channel_unlock(chan);
1014
1016 return 0;
1017}
1018
1019static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
1020{
1021 struct ast_datastore *datastore;
1022
1023 ast_channel_lock(chan);
1024
1025 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
1026
1027 /*
1028 * Currently the one place this function is called from guarantees a
1029 * datastore is present, thus return checks can be avoided here.
1030 */
1031 ast_channel_datastore_remove(chan, datastore);
1032 ast_datastore_free(datastore);
1033
1034 ast_channel_unlock(chan);
1035}
1036
1037static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
1038 unsigned int flags, int readvol, int writevol,
1039 const char *post_process, const char *filename_write,
1040 char *filename_read, const char *uid_channel_var,
1041 const char *recipients, const char *beep_id)
1042{
1043 pthread_t thread;
1044 struct mixmonitor *mixmonitor;
1045 char postprocess2[1024] = "";
1046 char *datastore_id = NULL;
1047
1048 postprocess2[0] = 0;
1049 /* If a post process system command is given attach it to the structure */
1051 char *p1, *p2;
1052
1054 for (p2 = p1; *p2; p2++) {
1055 if (*p2 == '^' && *(p2+1) == '{') {
1056 *p2 = '$';
1057 }
1058 }
1059 ast_channel_lock(chan);
1060 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
1061 ast_channel_unlock(chan);
1062 }
1063
1064 /* Pre-allocate mixmonitor structure and spy */
1065 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
1066 return -1;
1067 }
1068
1069 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
1072 return -1;
1073 }
1074
1075 /* Setup the actual spy before creating our thread */
1078 return -1;
1079 }
1080
1081 /* Copy over flags and channel name */
1083 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1085 return -1;
1086 }
1087
1088 if (!ast_strlen_zero(filename)) {
1090 }
1091
1094 }
1095
1098 }
1099
1100 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1103 ast_free(datastore_id);
1104 return -1;
1105 }
1106
1107 if (!ast_strlen_zero(uid_channel_var)) {
1108 if (datastore_id) {
1109 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1110 }
1111 }
1112
1114
1115 if (!ast_strlen_zero(postprocess2)) {
1116 mixmonitor->post_process = ast_strdup(postprocess2);
1117 }
1118
1119 if (!ast_strlen_zero(recipients)) {
1120 char callerid[256];
1121
1122 ast_channel_lock(chan);
1123
1124 /* We use the connected line of the invoking channel for caller ID,
1125 * unless we've been told to use the Caller ID.
1126 * The initial use for this relied on Connected Line to get the
1127 * actual number for recording with Digium phones,
1128 * but in generic use the Caller ID is likely what people want.
1129 */
1130
1132 struct ast_party_caller *caller;
1133 caller = ast_channel_caller(chan);
1134 ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1135 caller->id.name.str, caller->id.number.valid,
1136 caller->id.number.str);
1137 ast_callerid_merge(callerid, sizeof(callerid),
1138 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1139 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1140 "Unknown");
1141 } else {
1144 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1145 connected->id.name.str, connected->id.number.valid,
1146 connected->id.number.str);
1147 ast_callerid_merge(callerid, sizeof(callerid),
1148 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1149 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1150 "Unknown");
1151 }
1152
1154 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1155 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1156 ast_string_field_set(mixmonitor, call_callerid, callerid);
1158
1159 ast_channel_unlock(chan);
1160
1162 }
1163
1167 }
1168
1169 if (readvol)
1171 if (writevol)
1173
1174 if (startmon(chan, &mixmonitor->audiohook)) {
1175 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1177 mixmonitor_ds_remove_and_free(chan, datastore_id);
1178 ast_free(datastore_id);
1182 return -1;
1183 }
1184
1185 ast_free(datastore_id);
1186
1187 /* reference be released at mixmonitor destruction */
1189
1191}
1192
1193/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1194/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1195static char *filename_parse(char *filename, char *buffer, size_t len)
1196{
1197 char *slash;
1198 char *ext;
1199
1200 ast_assert(len > 0);
1201
1202 if (ast_strlen_zero(filename)) {
1203 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1204 buffer[0] = 0;
1205 return buffer;
1206 }
1207
1208 /* If we don't have an absolute path, make one */
1209 if (*filename != '/') {
1210 char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1211 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1212 filename = build;
1213 }
1214
1215 ast_copy_string(buffer, filename, len);
1216
1217 /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1218 match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1219 ends up referring to a file that does not/will not exist */
1220 ext = strrchr(buffer, '.');
1221 if (ext && !strcmp(ext, ".wav49")) {
1222 /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1223 * so this is safe */
1224 memcpy(ext, ".WAV", sizeof(".WAV"));
1225 }
1226
1227 if ((slash = strrchr(filename, '/'))) {
1228 *slash = '\0';
1229 }
1230 ast_mkdir(filename, 0777);
1231
1232 return buffer;
1233}
1234
1235static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1236{
1237 int x, readvol = 0, writevol = 0;
1238 char *filename_read = NULL;
1239 char *filename_write = NULL;
1240 char filename_buffer[1024] = "";
1241 char *uid_channel_var = NULL;
1242 char beep_id[64] = "";
1243
1244 struct ast_flags flags = { 0 };
1245 char *recipients = NULL;
1246 char *parse;
1249 AST_APP_ARG(filename);
1251 AST_APP_ARG(post_process);
1252 );
1253
1254 if (ast_strlen_zero(data)) {
1255 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1256 return -1;
1257 }
1258
1259 parse = ast_strdupa(data);
1260
1262
1263 if (args.options) {
1264 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1265
1267
1269 ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1270 " and does not need to be specified.\n");
1271 }
1272
1275 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1276 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1277 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1278 } else {
1279 readvol = get_volfactor(x);
1280 }
1281 }
1282
1285 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1286 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1287 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1288 } else {
1289 writevol = get_volfactor(x);
1290 }
1291 }
1292
1294 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1295 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1296 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1297 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1298 } else {
1299 readvol = writevol = get_volfactor(x);
1300 }
1301 }
1302
1305 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1306 } else {
1307 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1308 }
1309 }
1310
1312 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1313 }
1314
1316 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1317 }
1318
1320 uid_channel_var = opts[OPT_ARG_UID];
1321 }
1322
1324 const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1325 unsigned int interval = 15;
1326
1327 if (sscanf(interval_str, "%30u", &interval) != 1) {
1328 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1329 interval_str, interval);
1330 }
1331
1332 if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1333 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1334 return -1;
1335 }
1336 }
1337 }
1338 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1339
1341 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1342 return -1;
1343 }
1344
1345 /* If filename exists, try to create directories for it */
1346 if (!(ast_strlen_zero(args.filename))) {
1347 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1348 }
1349
1350 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1351
1352 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1354 if (launch_monitor_thread(chan,
1355 args.filename,
1356 flags.flags,
1357 readvol,
1358 writevol,
1359 args.post_process,
1360 filename_write,
1361 filename_read,
1362 uid_channel_var,
1363 recipients,
1364 beep_id)) {
1366 }
1367
1370 if (message) {
1372 }
1373
1374 return 0;
1375}
1376
1377static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1378{
1379 struct ast_datastore *datastore = NULL;
1380 char *parse = "";
1382 const char *beep_id = NULL;
1384
1386 AST_APP_ARG(mixmonid);
1387 );
1388
1389 if (!ast_strlen_zero(data)) {
1390 parse = ast_strdupa(data);
1391 }
1392
1394
1395 ast_channel_lock(chan);
1396
1398 S_OR(args.mixmonid, NULL));
1399 if (!datastore) {
1400 ast_channel_unlock(chan);
1401 return -1;
1402 }
1403 mixmonitor_ds = datastore->data;
1404
1406
1407 /* closing the filestream here guarantees the file is available to the dialplan
1408 * after calling StopMixMonitor */
1410
1411 /* The mixmonitor thread may be waiting on the audiohook trigger.
1412 * In order to exit from the mixmonitor loop before waiting on channel
1413 * destruction, poke the audiohook trigger. */
1414 if (mixmonitor_ds->audiohook) {
1417 }
1422 }
1423
1426 }
1427
1429
1430 /* Remove the datastore so the monitor thread can exit */
1431 if (!ast_channel_datastore_remove(chan, datastore)) {
1432 ast_datastore_free(datastore);
1433 }
1434
1435 ast_channel_unlock(chan);
1436
1437 if (!ast_strlen_zero(beep_id)) {
1438 ast_beep_stop(chan, beep_id);
1439 }
1440
1443 NULL);
1444 if (message) {
1446 }
1447
1448 return 0;
1449}
1450
1451static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1452{
1453 stop_mixmonitor_full(chan, data);
1454 return 0;
1455}
1456
1457static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1458{
1459 struct ast_channel *chan;
1460 struct ast_datastore *datastore = NULL;
1462
1463 switch (cmd) {
1464 case CLI_INIT:
1465 e->command = "mixmonitor {start|stop|list}";
1466 e->usage =
1467 "Usage: mixmonitor start <chan_name> [args]\n"
1468 " The optional arguments are passed to the MixMonitor application.\n"
1469 " mixmonitor stop <chan_name> [args]\n"
1470 " The optional arguments are passed to the StopMixMonitor application.\n"
1471 " mixmonitor list <chan_name>\n";
1472 return NULL;
1473 case CLI_GENERATE:
1474 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1475 }
1476
1477 if (a->argc < 3) {
1478 return CLI_SHOWUSAGE;
1479 }
1480
1481 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1482 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1483 /* Technically this is a failure, but we don't want 2 errors printing out */
1484 return CLI_SUCCESS;
1485 }
1486
1487 if (!strcasecmp(a->argv[1], "start")) {
1488 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1489 } else if (!strcasecmp(a->argv[1], "stop")){
1490 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1491 } else if (!strcasecmp(a->argv[1], "list")) {
1492 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1493 ast_cli(a->fd, "=========================================================================\n");
1494 ast_channel_lock(chan);
1495 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1496 if (datastore->info == &mixmonitor_ds_info) {
1497 char *filename = "";
1498 char *filename_read = "";
1499 char *filename_write = "";
1500
1501 mixmonitor_ds = datastore->data;
1502 if (mixmonitor_ds->fs) {
1504 }
1505 if (mixmonitor_ds->fs_read) {
1506 filename_read = mixmonitor_ds->fs_read->filename;
1507 }
1508 if (mixmonitor_ds->fs_write) {
1509 filename_write = mixmonitor_ds->fs_write->filename;
1510 }
1511 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1512 }
1513 }
1514 ast_channel_unlock(chan);
1515 } else {
1516 chan = ast_channel_unref(chan);
1517 return CLI_SHOWUSAGE;
1518 }
1519
1520 chan = ast_channel_unref(chan);
1521
1522 return CLI_SUCCESS;
1523}
1524
1525/*! \brief Mute / unmute an individual MixMonitor by id */
1526static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1527 enum ast_audiohook_flags flag, int clearmute)
1528{
1529 struct ast_datastore *datastore = NULL;
1530 char *parse = "";
1532
1534 AST_APP_ARG(mixmonid);
1535 );
1536
1537 if (!ast_strlen_zero(data)) {
1538 parse = ast_strdupa(data);
1539 }
1540
1542
1543 ast_channel_lock(chan);
1544
1546 S_OR(args.mixmonid, NULL));
1547 if (!datastore) {
1548 ast_channel_unlock(chan);
1549 return -1;
1550 }
1551 mixmonitor_ds = datastore->data;
1552
1554
1555 if (mixmonitor_ds->audiohook) {
1556 if (clearmute) {
1558 } else {
1560 }
1561 }
1562
1564 ast_channel_unlock(chan);
1565
1566 return 0;
1567}
1568
1569/*! \brief Mute / unmute a MixMonitor channel */
1570static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1571{
1572 struct ast_channel *c;
1573 const char *name = astman_get_header(m, "Channel");
1574 const char *id = astman_get_header(m, "ActionID");
1575 const char *state = astman_get_header(m, "State");
1576 const char *direction = astman_get_header(m,"Direction");
1577 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1578 int clearmute = 1, mutedcount = 0;
1581 RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1582
1584 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1585 return AMI_SUCCESS;
1586 }
1587
1588 if (!strcasecmp(direction, "read")) {
1590 } else if (!strcasecmp(direction, "write")) {
1592 } else if (!strcasecmp(direction, "both")) {
1594 } else {
1595 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1596 return AMI_SUCCESS;
1597 }
1598
1599 if (ast_strlen_zero(name)) {
1600 astman_send_error(s, m, "No channel specified");
1601 return AMI_SUCCESS;
1602 }
1603
1604 if (ast_strlen_zero(state)) {
1605 astman_send_error(s, m, "No state specified");
1606 return AMI_SUCCESS;
1607 }
1608
1609 clearmute = ast_false(state);
1610
1612 if (!c) {
1613 astman_send_error(s, m, "No such channel");
1614 return AMI_SUCCESS;
1615 }
1616
1617 if (ast_strlen_zero(mixmonitor_id)) {
1618 mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1619 if (mutedcount < 0) {
1621 astman_send_error(s, m, "Cannot set mute flag");
1622 return AMI_SUCCESS;
1623 }
1624 } else {
1625 if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1627 astman_send_error(s, m, "Cannot set mute flag");
1628 return AMI_SUCCESS;
1629 }
1630 mutedcount = 1;
1631 }
1632
1633
1634 stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1635 "direction", direction,
1636 "state", ast_true(state),
1637 "mixmonitorid", mixmonitor_id,
1638 "count", mutedcount);
1639
1641 ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1642
1643 if (stasis_message) {
1645 }
1646
1647 astman_append(s, "Response: Success\r\n");
1648
1649 if (!ast_strlen_zero(id)) {
1650 astman_append(s, "ActionID: %s\r\n", id);
1651 }
1652
1653 astman_append(s, "\r\n");
1654
1656
1657 return AMI_SUCCESS;
1658}
1659
1660static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1661{
1662 char args[PATH_MAX];
1663
1664 if (ast_strlen_zero(options)) {
1665 snprintf(args, sizeof(args), "%s", filename);
1666 } else {
1667 snprintf(args, sizeof(args), "%s,%s", filename, options);
1668 }
1669
1670 return mixmonitor_exec(chan, args);
1671}
1672
1673static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1674{
1675 return stop_mixmonitor_full(chan, mixmonitor_id);
1676}
1677
1678static int manager_mixmonitor(struct mansession *s, const struct message *m)
1679{
1680 struct ast_channel *c;
1681 const char *name = astman_get_header(m, "Channel");
1682 const char *id = astman_get_header(m, "ActionID");
1683 const char *file = astman_get_header(m, "File");
1684 const char *options = astman_get_header(m, "Options");
1685 const char *command = astman_get_header(m, "Command");
1686 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1687 struct ast_flags flags = { 0 };
1688 char *uid_channel_var = NULL;
1689 const char *mixmonitor_id = NULL;
1690 int res;
1691 char args[PATH_MAX];
1692
1693 if (ast_strlen_zero(name)) {
1694 astman_send_error(s, m, "No channel specified");
1695 return AMI_SUCCESS;
1696 }
1697
1699 if (!c) {
1700 astman_send_error(s, m, "No such channel");
1701 return AMI_SUCCESS;
1702 }
1703
1704 if (!ast_strlen_zero(options)) {
1706 }
1707
1708 snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1709
1710 res = mixmonitor_exec(c, args);
1711
1713 uid_channel_var = opts[OPT_ARG_UID];
1715 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1716 mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1718 }
1719
1720 if (res) {
1722 astman_send_error(s, m, "Could not start monitoring channel");
1723 return AMI_SUCCESS;
1724 }
1725
1726 astman_append(s, "Response: Success\r\n");
1727
1728 if (!ast_strlen_zero(id)) {
1729 astman_append(s, "ActionID: %s\r\n", id);
1730 }
1731
1732 if (!ast_strlen_zero(mixmonitor_id)) {
1733 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1734 }
1735
1736 astman_append(s, "\r\n");
1737
1739
1740 return AMI_SUCCESS;
1741}
1742
1743static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1744{
1745 struct ast_channel *c;
1746 const char *name = astman_get_header(m, "Channel");
1747 const char *id = astman_get_header(m, "ActionID");
1748 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1749 int res;
1750
1751 if (ast_strlen_zero(name)) {
1752 astman_send_error(s, m, "No channel specified");
1753 return AMI_SUCCESS;
1754 }
1755
1757 if (!c) {
1758 astman_send_error(s, m, "No such channel");
1759 return AMI_SUCCESS;
1760 }
1761
1762 res = stop_mixmonitor_full(c, mixmonitor_id);
1763 if (res) {
1765 astman_send_error(s, m, "Could not stop monitoring channel");
1766 return AMI_SUCCESS;
1767 }
1768
1769 astman_append(s, "Response: Success\r\n");
1770
1771 if (!ast_strlen_zero(id)) {
1772 astman_append(s, "ActionID: %s\r\n", id);
1773 }
1774
1775 astman_append(s, "\r\n");
1776
1778
1779 return AMI_SUCCESS;
1780}
1781
1782static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1783 char *buf, size_t len)
1784{
1785 struct ast_datastore *datastore;
1786 struct mixmonitor_ds *ds_data;
1788 AST_APP_ARG(id);
1789 AST_APP_ARG(key);
1790 );
1791
1793
1794 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1795 ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1796 "An ID and key must be provided\n", cmd);
1797 return -1;
1798 }
1799
1800 ast_channel_lock(chan);
1801 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1802 ast_channel_unlock(chan);
1803
1804 if (!datastore) {
1805 ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1806 return -1;
1807 }
1808
1809 ds_data = datastore->data;
1810
1811 if (!strcasecmp(args.key, "filename")) {
1812 ast_copy_string(buf, ds_data->filename, len);
1813 } else {
1814 ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
1815 return -1;
1816 }
1817 return 0;
1818}
1819
1821 .name = "MIXMONITOR",
1822 .read = func_mixmonitor_read,
1823};
1824
1826 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1827};
1828
1830{
1834 };
1835
1837}
1838
1840{
1842}
1843
1844static int unload_module(void)
1845{
1846 int res;
1847
1851 res |= ast_manager_unregister("MixMonitorMute");
1852 res |= ast_manager_unregister("MixMonitor");
1853 res |= ast_manager_unregister("StopMixMonitor");
1855 res |= clear_mixmonitor_methods();
1856
1857 return res;
1858}
1859
1860static int load_module(void)
1861{
1862 int res;
1863
1871 res |= set_mixmonitor_methods();
1872
1873 return res;
1874}
1875
1876AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
1877 .support_level = AST_MODULE_SUPPORT_CORE,
1878 .load = load_module,
1879 .unload = unload_module,
1880 .optional_modules = "func_periodic_hook",
static int mixmonitor_exec(struct ast_channel *chan, const char *data)
static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
static char * filename_parse(char *filename, char *buffer, size_t len)
static struct ast_cli_entry cli_mixmonitor[]
static struct ast_custom_function mixmonitor_function
static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
static const struct ast_datastore_info mixmonitor_ds_info
mixmonitor_args
@ OPT_ARG_WRITEVOLUME
@ OPT_ARG_UID
@ OPT_ARG_VMRECIPIENTS
@ OPT_ARG_DEPRECATED_RWSYNC
@ OPT_ARG_NO_RWSYNC
@ OPT_ARG_WRITENAME
@ OPT_ARG_READNAME
@ OPT_ARG_BEEP_INTERVAL
@ OPT_ARG_READVOLUME
@ OPT_ARG_VOLUME
@ OPT_ARG_ARRAY_SIZE
static int set_mixmonitor_methods(void)
static const struct ast_app_option mixmonitor_opts[128]
static const char *const app
static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
static int mixmonitor_autochan_is_bridged(struct ast_autochan *autochan)
static int launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process, const char *filename_write, char *filename_read, const char *uid_channel_var, const char *recipients, const char *beep_id)
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
Mute / unmute a MixMonitor channel.
static int clear_mixmonitor_methods(void)
static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data, enum ast_audiohook_flags flag, int clearmute)
Mute / unmute an individual MixMonitor by id.
static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
static char * handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define get_volfactor(x)
static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
#define SAMPLES_PER_FRAME
static void mixmonitor_free(struct mixmonitor *mixmonitor)
static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static const char *const stop_app
static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
static void * mixmonitor_thread(void *obj)
static int load_module(void)
static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
static int manager_mixmonitor(struct mansession *s, const struct message *m)
static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
static int unload_module(void)
static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
mixmonitor_flags
@ MUXFLAG_VMRECIPIENTS
@ MUXFLAG_READ
@ MUXFLAG_AUTO_DELETE
@ MUXFLAG_DEPRECATED_RWSYNC
@ MUXFLAG_REAL_CALLERID
@ MUXFLAG_WRITEVOLUME
@ MUXFLAG_INTERLEAVED
@ MUXFLAG_NO_RWSYNC
@ MUXFLAG_VOLUME
@ MUXFLAG_BEEP_STOP
@ MUXFLAG_UID
@ MUXFLAG_APPEND
@ MUXFLAG_BEEP_START
@ MUXFLAG_WRITE
@ MUXFLAG_COMBINED
@ MUXFLAG_BEEP
@ MUXFLAG_BRIDGED
@ MUXFLAG_READVOLUME
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
static const char *const mixmonitor_spy_type
static void mixmonitor_ds_destroy(void *data)
static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
pthread_t thread
Definition: app_sla.c:335
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition: asterisk.h:40
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
Audiohooks Architecture.
struct ast_frame * ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame)
Reads a frame in from the audiohook structure in mixed audio mode and copies read and write frame dat...
Definition: audiohook.c: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
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:2355
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2364
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:2972
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:1381
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition: channel.c:1398
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition: channel.c:10559
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3008
#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:2973
#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:2369
size_t current
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: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
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: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.
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: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.
@ 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: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).
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: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
#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.
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: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
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
const struct ast_datastore_info * info
Definition: datastore.h:67
void * data
Definition: datastore.h:66
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
struct ast_format_def * fmt
Definition: mod_format.h:103
char * filename
Definition: mod_format.h:107
Structure used to handle boolean flags.
Definition: utils.h:217
unsigned int flags
Definition: utils.h:218
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::@231 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 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
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
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:978
#define ast_assert(a)
Definition: utils.h:776
#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:2515
#define ast_pthread_create_detached_background(a, b, c, d)
Definition: utils.h:634
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:703
#define MAX(a, b)
Definition: utils.h:251