Asterisk - The Open Source Telephony Project GIT-master-a358458
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="n">
131 <para>When the <replaceable>r</replaceable> or <replaceable>t</replaceable> option is
132 used, MixMonitor will insert silence into the specified files to maintain
133 synchronization between them. Use this option to disable that behavior.</para>
134 </option>
135 <option name="i">
136 <argument name="chanvar" required="true" />
137 <para>Stores the MixMonitor's ID on this channel variable.</para>
138 </option>
139 <option name="p">
140 <para>Play a beep on the channel that starts the recording.</para>
141 </option>
142 <option name="P">
143 <para>Play a beep on the channel that stops the recording.</para>
144 </option>
145 <option name="m">
146 <argument name="mailbox" required="true" />
147 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
148 separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
149 the syntax: mailbox@context/folder</para>
150 </option>
151 </optionlist>
152 </parameter>
153 <parameter name="command">
154 <para>Will be executed when the recording is over.</para>
155 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
156 <para>All variables will be evaluated at the time MixMonitor is called.</para>
157 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
158 or <variable>CALLERID(name)</variable> as part of the command parameters. You
159 risk a command injection attack executing arbitrary commands if the untrusted
160 strings aren't filtered to remove dangerous characters. See function
161 <variable>FILTER()</variable>.</para></warning>
162 </parameter>
163 </syntax>
164 <description>
165 <para>Records the audio on the current channel to the specified file.</para>
166 <para>This application does not automatically answer and should be preceeded by
167 an application such as Answer or Progress().</para>
168 <note><para>MixMonitor runs as an audiohook.</para></note>
169 <note><para>If a filename passed to MixMonitor ends with
170 <literal>.wav49</literal>, Asterisk will silently convert the extension to
171 <literal>.WAV</literal> for legacy reasons. <variable>MIXMONITOR_FILENAME</variable>
172 will contain the actual filename that Asterisk is writing to, not necessarily the
173 value that was passed in.</para></note>
174 <variablelist>
175 <variable name="MIXMONITOR_FILENAME">
176 <para>Will contain the filename used to record.</para>
177 </variable>
178 </variablelist>
179 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
180 or <variable>CALLERID(name)</variable> as part of ANY of the application's
181 parameters. You risk a command injection attack executing arbitrary commands
182 if the untrusted strings aren't filtered to remove dangerous characters. See
183 function <variable>FILTER()</variable>.</para></warning>
184 </description>
185 <see-also>
186 <ref type="application">StopMixMonitor</ref>
187 </see-also>
188 </application>
189 <application name="StopMixMonitor" language="en_US">
190 <synopsis>
191 Stop recording a call through MixMonitor, and free the recording's file handle.
192 </synopsis>
193 <syntax>
194 <parameter name="MixMonitorID" required="false">
195 <para>If a valid ID is provided, then this command will stop only that specific
196 MixMonitor.</para>
197 </parameter>
198 </syntax>
199 <description>
200 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
201 on the current channel.</para>
202 </description>
203 <see-also>
204 <ref type="application">MixMonitor</ref>
205 </see-also>
206 </application>
207 <manager name="MixMonitorMute" language="en_US">
208 <synopsis>
209 Mute / unMute a Mixmonitor recording.
210 </synopsis>
211 <syntax>
212 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
213 <parameter name="Channel" required="true">
214 <para>Used to specify the channel to mute.</para>
215 </parameter>
216 <parameter name="Direction">
217 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
218 </parameter>
219 <parameter name="State">
220 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
221 </parameter>
222 </syntax>
223 <description>
224 <para>This action may be used to mute a MixMonitor recording.</para>
225 </description>
226 </manager>
227 <manager name="MixMonitor" language="en_US">
228 <synopsis>
229 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
230 to guarantee the audio file is available for processing during dialplan execution.
231 </synopsis>
232 <syntax>
233 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
234 <parameter name="Channel" required="true">
235 <para>Used to specify the channel to record.</para>
236 </parameter>
237 <parameter name="File">
238 <para>Is the name of the file created in the monitor spool directory.
239 Defaults to the same name as the channel (with slashes replaced with dashes).
240 This argument is optional if you specify to record unidirectional audio with
241 either the r(filename) or t(filename) options in the options field. If
242 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
243 be recorded.</para>
244 </parameter>
245 <parameter name="options">
246 <para>Options that apply to the MixMonitor in the same way as they
247 would apply if invoked from the MixMonitor application. For a list of
248 available options, see the documentation for the mixmonitor application. </para>
249 </parameter>
250 <parameter name="Command">
251 <para>Will be executed when the recording is over.
252 Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
253 All variables will be evaluated at the time MixMonitor is called.</para>
254 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
255 or <variable>CALLERID(name)</variable> as part of the command parameters. You
256 risk a command injection attack executing arbitrary commands if the untrusted
257 strings aren't filtered to remove dangerous characters. See function
258 <variable>FILTER()</variable>.</para></warning>
259 </parameter>
260 </syntax>
261 <description>
262 <para>This action records the audio on the current channel to the specified file.</para>
263 <variablelist>
264 <variable name="MIXMONITOR_FILENAME">
265 <para>Will contain the filename used to record the mixed stream.</para>
266 </variable>
267 </variablelist>
268 </description>
269 </manager>
270 <manager name="StopMixMonitor" language="en_US">
271 <synopsis>
272 Stop recording a call through MixMonitor, and free the recording's file handle.
273 </synopsis>
274 <syntax>
275 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
276 <parameter name="Channel" required="true">
277 <para>The name of the channel monitored.</para>
278 </parameter>
279 <parameter name="MixMonitorID" required="false">
280 <para>If a valid ID is provided, then this command will stop only that specific
281 MixMonitor.</para>
282 </parameter>
283 </syntax>
284 <description>
285 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
286 action on the current channel.</para>
287 </description>
288 </manager>
289 <function name="MIXMONITOR" language="en_US">
290 <synopsis>
291 Retrieve data pertaining to specific instances of MixMonitor on a channel.
292 </synopsis>
293 <syntax>
294 <parameter name="id" required="true">
295 <para>The unique ID of the MixMonitor instance. The unique ID can be retrieved through the channel
296 variable used as an argument to the <replaceable>i</replaceable> option to MixMonitor.</para>
297 </parameter>
298 <parameter name="key" required="true">
299 <para>The piece of data to retrieve from the MixMonitor.</para>
300 <enumlist>
301 <enum name="filename" />
302 </enumlist>
303 </parameter>
304 </syntax>
305 </function>
306 <managerEvent language="en_US" name="MixMonitorStart">
307 <managerEventInstance class="EVENT_FLAG_CALL">
308 <synopsis>Raised when monitoring has started on a channel.</synopsis>
309 <syntax>
310 <channel_snapshot/>
311 </syntax>
312 <see-also>
313 <ref type="managerEvent">MixMonitorStop</ref>
314 <ref type="application">MixMonitor</ref>
315 <ref type="manager">MixMonitor</ref>
316 </see-also>
317 </managerEventInstance>
318 </managerEvent>
319 <managerEvent language="en_US" name="MixMonitorStop">
320 <managerEventInstance class="EVENT_FLAG_CALL">
321 <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
322 <syntax>
323 <channel_snapshot/>
324 </syntax>
325 <see-also>
326 <ref type="managerEvent">MixMonitorStart</ref>
327 <ref type="application">StopMixMonitor</ref>
328 <ref type="manager">StopMixMonitor</ref>
329 </see-also>
330 </managerEventInstance>
331 </managerEvent>
332 <managerEvent language="en_US" name="MixMonitorMute">
333 <managerEventInstance class="EVENT_FLAG_CALL">
334 <synopsis>Raised when monitoring is muted or unmuted on a channel.</synopsis>
335 <syntax>
336 <channel_snapshot/>
337 <parameter name="Direction">
338 <para>Which part of the recording was muted or unmuted: read, write or both
339 (from channel, to channel or both directions).</para>
340 </parameter>
341 <parameter name="State">
342 <para>If the monitoring was muted or unmuted: 1 when muted, 0 when unmuted.</para>
343 </parameter>
344 </syntax>
345 <see-also>
346 <ref type="manager">MixMonitorMute</ref>
347 </see-also>
348 </managerEventInstance>
349 </managerEvent>
350
351
352 ***/
353
354#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
355
356static const char * const app = "MixMonitor";
357
358static const char * const stop_app = "StopMixMonitor";
359
360static const char * const mixmonitor_spy_type = "MixMonitor";
361
362/*!
363 * \internal
364 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
365 */
369 char folder[80];
371};
372
375 char *filename;
379 char *name;
381 unsigned int flags;
384
385 /* the below string fields describe data used for creating voicemails from the recording */
391 );
393
394 /* FUTURE DEVELOPMENT NOTICE
395 * recipient_list will need locks if we make it editable after the monitor is started */
397};
398
400 MUXFLAG_APPEND = (1 << 1),
401 MUXFLAG_BRIDGED = (1 << 2),
402 MUXFLAG_VOLUME = (1 << 3),
405 MUXFLAG_READ = (1 << 6),
406 MUXFLAG_WRITE = (1 << 7),
408 MUXFLAG_UID = (1 << 9),
410 MUXFLAG_BEEP = (1 << 11),
412 MUXFLAG_BEEP_STOP = (1 << 13),
414 MUXFLAG_NO_RWSYNC = (1 << 15),
417};
418
430 OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
431};
432
450});
451
453 unsigned int destruction_ok;
456
457 /* The filestream is held in the datastore so it can be stopped
458 * immediately during stop_mixmonitor or channel destruction. */
460
464
466
467 unsigned int samp_rate;
468 char *filename;
469 char *beep_id;
470};
471
472/*!
473 * \internal
474 * \pre mixmonitor_ds must be locked before calling this function
475 */
477{
478 unsigned char quitting = 0;
479
480 if (mixmonitor_ds->fs) {
481 quitting = 1;
484 ast_verb(2, "MixMonitor close filestream (mixed)\n");
485 }
486
487 if (mixmonitor_ds->fs_read) {
488 quitting = 1;
491 ast_verb(2, "MixMonitor close filestream (read)\n");
492 }
493
494 if (mixmonitor_ds->fs_write) {
495 quitting = 1;
498 ast_verb(2, "MixMonitor close filestream (write)\n");
499 }
500
501 if (quitting) {
503 }
504}
505
506static void mixmonitor_ds_destroy(void *data)
507{
508 struct mixmonitor_ds *mixmonitor_ds = data;
509
517}
518
520 .type = "mixmonitor",
521 .destroy = mixmonitor_ds_destroy,
522};
523
525{
530 }
531 /* kill the audiohook.*/
536}
537
538static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
539{
540 if (!chan) {
541 return -1;
542 }
543
544 return ast_audiohook_attach(chan, audiohook);
545}
546
547/*!
548 * \internal
549 * \brief adds recipients to a mixmonitor's recipient list
550 * \param mixmonitor mixmonitor being affected
551 * \param vm_recipients string containing the desired recipients to add
552 */
553static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
554{
555 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
556 char *cur_mailbox = ast_strdupa(vm_recipients);
557 char *cur_context;
558 char *cur_folder;
559 char *next;
560 int elements_processed = 0;
561
562 while (!ast_strlen_zero(cur_mailbox)) {
563 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
564 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
565 *(next++) = '\0';
566 }
567
568 if ((cur_folder = strchr(cur_mailbox, '/'))) {
569 *(cur_folder++) = '\0';
570 } else {
571 cur_folder = "INBOX";
572 }
573
574 if ((cur_context = strchr(cur_mailbox, '@'))) {
575 *(cur_context++) = '\0';
576 } else {
577 cur_context = "default";
578 }
579
580 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
581 struct vm_recipient *recipient;
582 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
583 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
584 return;
585 }
586 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
587 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
588 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
589
590 /* Add to list */
591 ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
593 } else {
594 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
595 }
596
597 cur_mailbox = next;
598 elements_processed++;
599 }
600}
601
603{
604 struct vm_recipient *current;
606 /* Clear list element data */
608 }
609}
610
611#define SAMPLES_PER_FRAME 160
612
614{
615 if (mixmonitor) {
620 }
621
627
628 /* Free everything in the recipient list */
630
631 /* clean stringfields */
633
635 }
636}
637
638/*!
639 * \internal
640 * \brief Copies the mixmonitor to all voicemail recipients
641 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
642 * \param ext Format of the file that was saved
643 * \param filename
644 */
645static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
646{
647 struct vm_recipient *recipient = NULL;
648 struct ast_vm_recording_data recording_data;
649 if (ast_string_field_init(&recording_data, 512)) {
650 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
651 return;
652 }
653
654 /* Copy strings to stringfields that will be used for all recipients */
655 ast_string_field_set(&recording_data, recording_file, filename);
656 ast_string_field_set(&recording_data, recording_ext, ext);
661 /* and call_priority gets copied too */
662 recording_data.call_priority = mixmonitor->call_priority;
663
664 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
665 /* context, mailbox, and folder need to be set per recipient */
666 ast_string_field_set(&recording_data, context, recipient->context);
667 ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
668 ast_string_field_set(&recording_data, folder, recipient->folder);
669
670 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
671 recording_data.context);
672 ast_app_copy_recording_to_vm(&recording_data);
673 }
674
675 /* Free the string fields for recording_data before exiting the function. */
676 ast_string_field_free_memory(&recording_data);
677}
678
679static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
680{
681 /* Initialize the file if not already done so */
682 char *last_slash = NULL;
683 if (!ast_strlen_zero(filename)) {
684 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
685 *oflags = O_CREAT | O_WRONLY;
686 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
687
688 last_slash = strrchr(filename, '/');
689
690 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
691 **ext = '\0';
692 *ext = *ext + 1;
693 } else {
694 *ext = "raw";
695 }
696
697 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
698 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
699 *errflag = 1;
700 } else {
701 struct ast_filestream *tmp = *fs;
703 }
704 }
705 }
706}
707
709{
710 int is_bridged;
711
713 is_bridged = ast_channel_is_bridged(autochan->chan);
715 return is_bridged;
716}
717
718static void *mixmonitor_thread(void *obj)
719{
720 struct mixmonitor *mixmonitor = obj;
721 char *fs_ext = "";
722 char *fs_read_ext = "";
723 char *fs_write_ext = "";
724
725 struct ast_filestream **fs = NULL;
726 struct ast_filestream **fs_read = NULL;
727 struct ast_filestream **fs_write = NULL;
728
729 unsigned int oflags;
730 int errflag = 0;
731 struct ast_format *format_slin;
732
733 /* Keep callid association before any log messages */
734 if (mixmonitor->callid) {
736 }
737
738 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
739
741 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
742 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
743
745 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
746 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
747 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
748
750
752
753 /* The audiohook must enter and exit the loop locked */
756 struct ast_frame *fr = NULL;
757 struct ast_frame *fr_read = NULL;
758 struct ast_frame *fr_write = NULL;
759
761 &fr_read, &fr_write))) {
763
765 break;
766 }
767 continue;
768 }
769
770 /* audiohook lock is not required for the next block.
771 * Unlock it, but remember to lock it before looping or exiting */
773
777
778 /* Write out the frame(s) */
779 if ((*fs_read) && (fr_read)) {
780 struct ast_frame *cur;
781
782 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
783 ast_writestream(*fs_read, cur);
784 }
785 }
786
787 if ((*fs_write) && (fr_write)) {
788 struct ast_frame *cur;
789
790 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
791 ast_writestream(*fs_write, cur);
792 }
793 }
794
795 if ((*fs) && (fr)) {
796 struct ast_frame *cur;
797
798 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
799 ast_writestream(*fs, cur);
800 }
801 }
803 }
804 /* All done! free it. */
805 if (fr) {
806 ast_frame_free(fr, 0);
807 }
808 if (fr_read) {
809 ast_frame_free(fr_read, 0);
810 }
811 if (fr_write) {
812 ast_frame_free(fr_write, 0);
813 }
814
815 fr = NULL;
816 fr_write = NULL;
817 fr_read = NULL;
818
820 }
821
823
828 }
829
831
832 /* Datastore cleanup. close the filestream and wait for ds destruction */
837 }
839
840 /* kill the audiohook */
842
844 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
846 }
847
848 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
849 ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
850
852 if (ast_strlen_zero(fs_ext)) {
853 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
854 mixmonitor -> name);
855 } else {
856 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
858 }
859 if (!ast_strlen_zero(fs_read_ext)) {
860 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
862 }
863 if (!ast_strlen_zero(fs_write_ext)) {
864 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
866 }
867 } else {
868 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
869 }
870
872 ast_debug(3, "Deleting our copies of recording files\n");
873 if (!ast_strlen_zero(fs_ext)) {
875 }
876 if (!ast_strlen_zero(fs_read_ext)) {
878 }
879 if (!ast_strlen_zero(fs_write_ext)) {
881 }
882 }
883
885
887 return NULL;
888}
889
890static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
891{
892 struct ast_datastore *datastore = NULL;
894
895 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
896 return -1;
897 }
898
899 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
900 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
902 return -1;
903 }
904
907
908 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
912 return -1;
913 }
914
919 }
920
921 mixmonitor_ds->samp_rate = 8000;
924 if (!ast_strlen_zero(beep_id)) {
926 }
927 datastore->data = mixmonitor_ds;
928
929 ast_channel_lock(chan);
930 ast_channel_datastore_add(chan, datastore);
931 ast_channel_unlock(chan);
932
934 return 0;
935}
936
937static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
938{
939 struct ast_datastore *datastore;
940
941 ast_channel_lock(chan);
942
943 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
944
945 /*
946 * Currently the one place this function is called from guarantees a
947 * datastore is present, thus return checks can be avoided here.
948 */
949 ast_channel_datastore_remove(chan, datastore);
950 ast_datastore_free(datastore);
951
952 ast_channel_unlock(chan);
953}
954
955static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
956 unsigned int flags, int readvol, int writevol,
957 const char *post_process, const char *filename_write,
958 char *filename_read, const char *uid_channel_var,
959 const char *recipients, const char *beep_id)
960{
961 pthread_t thread;
962 struct mixmonitor *mixmonitor;
963 char postprocess2[1024] = "";
964 char *datastore_id = NULL;
965
966 postprocess2[0] = 0;
967 /* If a post process system command is given attach it to the structure */
969 char *p1, *p2;
970
972 for (p2 = p1; *p2; p2++) {
973 if (*p2 == '^' && *(p2+1) == '{') {
974 *p2 = '$';
975 }
976 }
977 ast_channel_lock(chan);
978 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
979 ast_channel_unlock(chan);
980 }
981
982 /* Pre-allocate mixmonitor structure and spy */
983 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
984 return -1;
985 }
986
987 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
990 return -1;
991 }
992
993 /* Setup the actual spy before creating our thread */
996 return -1;
997 }
998
999 /* Copy over flags and channel name */
1001 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1003 return -1;
1004 }
1005
1006 if (!ast_strlen_zero(filename)) {
1008 }
1009
1012 }
1013
1016 }
1017
1018 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1021 ast_free(datastore_id);
1022 return -1;
1023 }
1024
1025 if (!ast_strlen_zero(uid_channel_var)) {
1026 if (datastore_id) {
1027 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1028 }
1029 }
1030
1032
1033 if (!ast_strlen_zero(postprocess2)) {
1034 mixmonitor->post_process = ast_strdup(postprocess2);
1035 }
1036
1037 if (!ast_strlen_zero(recipients)) {
1038 char callerid[256];
1039
1040 ast_channel_lock(chan);
1041
1042 /* We use the connected line of the invoking channel for caller ID,
1043 * unless we've been told to use the Caller ID.
1044 * The initial use for this relied on Connected Line to get the
1045 * actual number for recording with Digium phones,
1046 * but in generic use the Caller ID is likely what people want.
1047 */
1048
1050 struct ast_party_caller *caller;
1051 caller = ast_channel_caller(chan);
1052 ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1053 caller->id.name.str, caller->id.number.valid,
1054 caller->id.number.str);
1055 ast_callerid_merge(callerid, sizeof(callerid),
1056 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1057 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1058 "Unknown");
1059 } else {
1062 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1063 connected->id.name.str, connected->id.number.valid,
1064 connected->id.number.str);
1065 ast_callerid_merge(callerid, sizeof(callerid),
1066 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1067 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1068 "Unknown");
1069 }
1070
1072 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1073 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1074 ast_string_field_set(mixmonitor, call_callerid, callerid);
1076
1077 ast_channel_unlock(chan);
1078
1080 }
1081
1085 }
1086
1087 if (readvol)
1089 if (writevol)
1091
1092 if (startmon(chan, &mixmonitor->audiohook)) {
1093 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1095 mixmonitor_ds_remove_and_free(chan, datastore_id);
1096 ast_free(datastore_id);
1100 return -1;
1101 }
1102
1103 ast_free(datastore_id);
1104
1105 /* reference be released at mixmonitor destruction */
1107
1109}
1110
1111/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1112/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1113static char *filename_parse(char *filename, char *buffer, size_t len)
1114{
1115 char *slash;
1116 char *ext;
1117
1118 ast_assert(len > 0);
1119
1120 if (ast_strlen_zero(filename)) {
1121 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1122 buffer[0] = 0;
1123 return buffer;
1124 }
1125
1126 /* If we don't have an absolute path, make one */
1127 if (*filename != '/') {
1128 char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1129 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1130 filename = build;
1131 }
1132
1133 ast_copy_string(buffer, filename, len);
1134
1135 /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1136 match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1137 ends up referring to a file that does not/will not exist */
1138 ext = strrchr(buffer, '.');
1139 if (ext && !strcmp(ext, ".wav49")) {
1140 /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1141 * so this is safe */
1142 memcpy(ext, ".WAV", sizeof(".WAV"));
1143 }
1144
1145 if ((slash = strrchr(filename, '/'))) {
1146 *slash = '\0';
1147 }
1148 ast_mkdir(filename, 0777);
1149
1150 return buffer;
1151}
1152
1153static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1154{
1155 int x, readvol = 0, writevol = 0;
1156 char *filename_read = NULL;
1157 char *filename_write = NULL;
1158 char filename_buffer[1024] = "";
1159 char *uid_channel_var = NULL;
1160 char beep_id[64] = "";
1161
1162 struct ast_flags flags = { 0 };
1163 char *recipients = NULL;
1164 char *parse;
1167 AST_APP_ARG(filename);
1169 AST_APP_ARG(post_process);
1170 );
1171
1172 if (ast_strlen_zero(data)) {
1173 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1174 return -1;
1175 }
1176
1177 parse = ast_strdupa(data);
1178
1180
1181 if (args.options) {
1182 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1183
1185
1187 ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1188 " and does not need to be specified.\n");
1189 }
1190
1193 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1194 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1195 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1196 } else {
1197 readvol = get_volfactor(x);
1198 }
1199 }
1200
1203 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1204 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1205 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1206 } else {
1207 writevol = get_volfactor(x);
1208 }
1209 }
1210
1212 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1213 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1214 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1215 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1216 } else {
1217 readvol = writevol = get_volfactor(x);
1218 }
1219 }
1220
1223 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1224 } else {
1225 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1226 }
1227 }
1228
1230 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1231 }
1232
1234 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1235 }
1236
1238 uid_channel_var = opts[OPT_ARG_UID];
1239 }
1240
1242 const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1243 unsigned int interval = 15;
1244
1245 if (sscanf(interval_str, "%30u", &interval) != 1) {
1246 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1247 interval_str, interval);
1248 }
1249
1250 if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1251 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1252 return -1;
1253 }
1254 }
1255 }
1256 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1257
1259 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1260 return -1;
1261 }
1262
1263 /* If filename exists, try to create directories for it */
1264 if (!(ast_strlen_zero(args.filename))) {
1265 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1266 }
1267
1268 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1269
1270 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1272 if (launch_monitor_thread(chan,
1273 args.filename,
1274 flags.flags,
1275 readvol,
1276 writevol,
1277 args.post_process,
1278 filename_write,
1279 filename_read,
1280 uid_channel_var,
1281 recipients,
1282 beep_id)) {
1284 }
1285
1288 if (message) {
1290 }
1291
1292 return 0;
1293}
1294
1295static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1296{
1297 struct ast_datastore *datastore = NULL;
1298 char *parse = "";
1300 const char *beep_id = NULL;
1302
1304 AST_APP_ARG(mixmonid);
1305 );
1306
1307 if (!ast_strlen_zero(data)) {
1308 parse = ast_strdupa(data);
1309 }
1310
1312
1313 ast_channel_lock(chan);
1314
1316 S_OR(args.mixmonid, NULL));
1317 if (!datastore) {
1318 ast_channel_unlock(chan);
1319 return -1;
1320 }
1321 mixmonitor_ds = datastore->data;
1322
1324
1325 /* closing the filestream here guarantees the file is available to the dialplan
1326 * after calling StopMixMonitor */
1328
1329 /* The mixmonitor thread may be waiting on the audiohook trigger.
1330 * In order to exit from the mixmonitor loop before waiting on channel
1331 * destruction, poke the audiohook trigger. */
1332 if (mixmonitor_ds->audiohook) {
1335 }
1340 }
1341
1344 }
1345
1347
1348 /* Remove the datastore so the monitor thread can exit */
1349 if (!ast_channel_datastore_remove(chan, datastore)) {
1350 ast_datastore_free(datastore);
1351 }
1352
1353 ast_channel_unlock(chan);
1354
1355 if (!ast_strlen_zero(beep_id)) {
1356 ast_beep_stop(chan, beep_id);
1357 }
1358
1361 NULL);
1362 if (message) {
1364 }
1365
1366 return 0;
1367}
1368
1369static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1370{
1371 stop_mixmonitor_full(chan, data);
1372 return 0;
1373}
1374
1375static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1376{
1377 struct ast_channel *chan;
1378 struct ast_datastore *datastore = NULL;
1380
1381 switch (cmd) {
1382 case CLI_INIT:
1383 e->command = "mixmonitor {start|stop|list}";
1384 e->usage =
1385 "Usage: mixmonitor start <chan_name> [args]\n"
1386 " The optional arguments are passed to the MixMonitor application.\n"
1387 " mixmonitor stop <chan_name> [args]\n"
1388 " The optional arguments are passed to the StopMixMonitor application.\n"
1389 " mixmonitor list <chan_name>\n";
1390 return NULL;
1391 case CLI_GENERATE:
1392 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1393 }
1394
1395 if (a->argc < 3) {
1396 return CLI_SHOWUSAGE;
1397 }
1398
1399 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1400 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1401 /* Technically this is a failure, but we don't want 2 errors printing out */
1402 return CLI_SUCCESS;
1403 }
1404
1405 if (!strcasecmp(a->argv[1], "start")) {
1406 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1407 } else if (!strcasecmp(a->argv[1], "stop")){
1408 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1409 } else if (!strcasecmp(a->argv[1], "list")) {
1410 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1411 ast_cli(a->fd, "=========================================================================\n");
1412 ast_channel_lock(chan);
1414 if (datastore->info == &mixmonitor_ds_info) {
1415 char *filename = "";
1416 char *filename_read = "";
1417 char *filename_write = "";
1418
1419 mixmonitor_ds = datastore->data;
1420 if (mixmonitor_ds->fs) {
1422 }
1423 if (mixmonitor_ds->fs_read) {
1424 filename_read = mixmonitor_ds->fs_read->filename;
1425 }
1426 if (mixmonitor_ds->fs_write) {
1427 filename_write = mixmonitor_ds->fs_write->filename;
1428 }
1429 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1430 }
1431 }
1432 ast_channel_unlock(chan);
1433 } else {
1434 chan = ast_channel_unref(chan);
1435 return CLI_SHOWUSAGE;
1436 }
1437
1438 chan = ast_channel_unref(chan);
1439
1440 return CLI_SUCCESS;
1441}
1442
1443/*! \brief Mute / unmute an individual MixMonitor by id */
1444static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1445 enum ast_audiohook_flags flag, int clearmute)
1446{
1447 struct ast_datastore *datastore = NULL;
1448 char *parse = "";
1450
1452 AST_APP_ARG(mixmonid);
1453 );
1454
1455 if (!ast_strlen_zero(data)) {
1456 parse = ast_strdupa(data);
1457 }
1458
1460
1461 ast_channel_lock(chan);
1462
1464 S_OR(args.mixmonid, NULL));
1465 if (!datastore) {
1466 ast_channel_unlock(chan);
1467 return -1;
1468 }
1469 mixmonitor_ds = datastore->data;
1470
1472
1473 if (mixmonitor_ds->audiohook) {
1474 if (clearmute) {
1476 } else {
1478 }
1479 }
1480
1482 ast_channel_unlock(chan);
1483
1484 return 0;
1485}
1486
1487/*! \brief Mute / unmute a MixMonitor channel */
1488static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1489{
1490 struct ast_channel *c;
1491 const char *name = astman_get_header(m, "Channel");
1492 const char *id = astman_get_header(m, "ActionID");
1493 const char *state = astman_get_header(m, "State");
1494 const char *direction = astman_get_header(m,"Direction");
1495 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1496 int clearmute = 1, mutedcount = 0;
1499 RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1500
1502 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1503 return AMI_SUCCESS;
1504 }
1505
1506 if (!strcasecmp(direction, "read")) {
1508 } else if (!strcasecmp(direction, "write")) {
1510 } else if (!strcasecmp(direction, "both")) {
1512 } else {
1513 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1514 return AMI_SUCCESS;
1515 }
1516
1517 if (ast_strlen_zero(name)) {
1518 astman_send_error(s, m, "No channel specified");
1519 return AMI_SUCCESS;
1520 }
1521
1522 if (ast_strlen_zero(state)) {
1523 astman_send_error(s, m, "No state specified");
1524 return AMI_SUCCESS;
1525 }
1526
1527 clearmute = ast_false(state);
1528
1530 if (!c) {
1531 astman_send_error(s, m, "No such channel");
1532 return AMI_SUCCESS;
1533 }
1534
1535 if (ast_strlen_zero(mixmonitor_id)) {
1536 mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1537 if (mutedcount < 0) {
1539 astman_send_error(s, m, "Cannot set mute flag");
1540 return AMI_SUCCESS;
1541 }
1542 } else {
1543 if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1545 astman_send_error(s, m, "Cannot set mute flag");
1546 return AMI_SUCCESS;
1547 }
1548 mutedcount = 1;
1549 }
1550
1551
1552 stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1553 "direction", direction,
1554 "state", ast_true(state),
1555 "mixmonitorid", mixmonitor_id,
1556 "count", mutedcount);
1557
1559 ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1560
1561 if (stasis_message) {
1563 }
1564
1565 astman_append(s, "Response: Success\r\n");
1566
1567 if (!ast_strlen_zero(id)) {
1568 astman_append(s, "ActionID: %s\r\n", id);
1569 }
1570
1571 astman_append(s, "\r\n");
1572
1574
1575 return AMI_SUCCESS;
1576}
1577
1578static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1579{
1580 char args[PATH_MAX];
1581
1582 if (ast_strlen_zero(options)) {
1583 snprintf(args, sizeof(args), "%s", filename);
1584 } else {
1585 snprintf(args, sizeof(args), "%s,%s", filename, options);
1586 }
1587
1588 return mixmonitor_exec(chan, args);
1589}
1590
1591static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1592{
1593 return stop_mixmonitor_full(chan, mixmonitor_id);
1594}
1595
1596static int manager_mixmonitor(struct mansession *s, const struct message *m)
1597{
1598 struct ast_channel *c;
1599 const char *name = astman_get_header(m, "Channel");
1600 const char *id = astman_get_header(m, "ActionID");
1601 const char *file = astman_get_header(m, "File");
1602 const char *options = astman_get_header(m, "Options");
1603 const char *command = astman_get_header(m, "Command");
1604 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1605 struct ast_flags flags = { 0 };
1606 char *uid_channel_var = NULL;
1607 const char *mixmonitor_id = NULL;
1608 int res;
1609 char args[PATH_MAX];
1610
1611 if (ast_strlen_zero(name)) {
1612 astman_send_error(s, m, "No channel specified");
1613 return AMI_SUCCESS;
1614 }
1615
1617 if (!c) {
1618 astman_send_error(s, m, "No such channel");
1619 return AMI_SUCCESS;
1620 }
1621
1622 if (!ast_strlen_zero(options)) {
1624 }
1625
1626 snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1627
1628 res = mixmonitor_exec(c, args);
1629
1631 uid_channel_var = opts[OPT_ARG_UID];
1633 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1634 mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1636 }
1637
1638 if (res) {
1640 astman_send_error(s, m, "Could not start monitoring channel");
1641 return AMI_SUCCESS;
1642 }
1643
1644 astman_append(s, "Response: Success\r\n");
1645
1646 if (!ast_strlen_zero(id)) {
1647 astman_append(s, "ActionID: %s\r\n", id);
1648 }
1649
1650 if (!ast_strlen_zero(mixmonitor_id)) {
1651 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1652 }
1653
1654 astman_append(s, "\r\n");
1655
1657
1658 return AMI_SUCCESS;
1659}
1660
1661static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1662{
1663 struct ast_channel *c;
1664 const char *name = astman_get_header(m, "Channel");
1665 const char *id = astman_get_header(m, "ActionID");
1666 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1667 int res;
1668
1669 if (ast_strlen_zero(name)) {
1670 astman_send_error(s, m, "No channel specified");
1671 return AMI_SUCCESS;
1672 }
1673
1675 if (!c) {
1676 astman_send_error(s, m, "No such channel");
1677 return AMI_SUCCESS;
1678 }
1679
1680 res = stop_mixmonitor_full(c, mixmonitor_id);
1681 if (res) {
1683 astman_send_error(s, m, "Could not stop monitoring channel");
1684 return AMI_SUCCESS;
1685 }
1686
1687 astman_append(s, "Response: Success\r\n");
1688
1689 if (!ast_strlen_zero(id)) {
1690 astman_append(s, "ActionID: %s\r\n", id);
1691 }
1692
1693 astman_append(s, "\r\n");
1694
1696
1697 return AMI_SUCCESS;
1698}
1699
1700static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1701 char *buf, size_t len)
1702{
1703 struct ast_datastore *datastore;
1704 struct mixmonitor_ds *ds_data;
1706 AST_APP_ARG(id);
1707 AST_APP_ARG(key);
1708 );
1709
1711
1712 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1713 ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1714 "An ID and key must be provided\n", cmd);
1715 return -1;
1716 }
1717
1718 ast_channel_lock(chan);
1719 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1720 ast_channel_unlock(chan);
1721
1722 if (!datastore) {
1723 ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1724 return -1;
1725 }
1726
1727 ds_data = datastore->data;
1728
1729 if (!strcasecmp(args.key, "filename")) {
1730 ast_copy_string(buf, ds_data->filename, len);
1731 } else {
1732 ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
1733 return -1;
1734 }
1735 return 0;
1736}
1737
1739 .name = "MIXMONITOR",
1740 .read = func_mixmonitor_read,
1741};
1742
1744 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1745};
1746
1748{
1752 };
1753
1755}
1756
1758{
1760}
1761
1762static int unload_module(void)
1763{
1764 int res;
1765
1769 res |= ast_manager_unregister("MixMonitorMute");
1770 res |= ast_manager_unregister("MixMonitor");
1771 res |= ast_manager_unregister("StopMixMonitor");
1773 res |= clear_mixmonitor_methods();
1774
1775 return res;
1776}
1777
1778static int load_module(void)
1779{
1780 int res;
1781
1789 res |= set_mixmonitor_methods();
1790
1791 return res;
1792}
1793
1794AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
1795 .support_level = AST_MODULE_SUPPORT_CORE,
1796 .load = load_module,
1797 .unload = unload_module,
1798 .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_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:1174
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:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
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:2922
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:1434
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition: channel.c:10545
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
#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:1454
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:2923
#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:2399
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:1860
@ 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
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:3381
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3042
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3302
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8041
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:3056
void ast_frame_free(struct ast_frame *frame, int cache)
Frees a frame or list of frames.
Definition: main/frame.c:176
#define ast_debug(level,...)
Log a DEBUG message.
ast_callid ast_read_threadstorage_callid(void)
extracts the callerid from the thread
Definition: logger.c:2286
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition: logger.c:2308
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:315
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ 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:626
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:1511
#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
Data structure associated with a single frame of 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:342
Structure for mutex and tracking information.
Definition: lock.h:135
Caller Party information.
Definition: channel.h:418
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
Connected Line/Party information.
Definition: channel.h:456
struct ast_party_name name
Subscriber name.
Definition: channel.h:340
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:279
char * str
Subscriber name (Malloced)
Definition: channel.h:264
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
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:1777
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