Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
app_confbridge.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2007-2008, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@digium.com>
7 * David Vossel <dvossel@digium.com>
8 *
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
14 *
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
18 */
19
20/*! \file
21 *
22 * \brief Conference Bridge application
23 *
24 * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
25 * \author\verbatim David Vossel <dvossel@digium.com> \endverbatim
26 *
27 * This is a conference bridge application utilizing the bridging core.
28 * \ingroup applications
29 */
30
31/*! \li \ref app_confbridge.c uses the configuration file \ref confbridge.conf
32 * \addtogroup configuration_file Configuration Files
33 */
34
35/*!
36 * \page confbridge.conf confbridge.conf
37 * \verbinclude confbridge.conf.sample
38 */
39
40/*** MODULEINFO
41 <support_level>core</support_level>
42 ***/
43
44#include "asterisk.h"
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <string.h>
50#include <signal.h>
51
52#include "asterisk/cli.h"
53#include "asterisk/file.h"
54#include "asterisk/channel.h"
55#include "asterisk/pbx.h"
56#include "asterisk/pbx.h"
57#include "asterisk/module.h"
58#include "asterisk/lock.h"
59#include "asterisk/bridge.h"
61#include "asterisk/say.h"
62#include "asterisk/audiohook.h"
63#include "asterisk/astobj2.h"
65#include "asterisk/paths.h"
66#include "asterisk/manager.h"
67#include "asterisk/test.h"
68#include "asterisk/stasis.h"
71#include "asterisk/json.h"
74#include "asterisk/stream.h"
75#include "asterisk/message.h"
76
77/*** DOCUMENTATION
78 <application name="ConfBridge" language="en_US">
79 <since>
80 <version>12.0.0</version>
81 </since>
82 <synopsis>
83 Conference bridge application.
84 </synopsis>
85 <syntax>
86 <parameter name="conference" required="true">
87 <para>Name of the conference bridge. You are not limited to just
88 numbers.</para>
89 </parameter>
90 <parameter name="bridge_profile">
91 <para>The bridge profile name from confbridge.conf. When left blank,
92 a dynamically built bridge profile created by the CONFBRIDGE dialplan
93 function is searched for on the channel and used. If no dynamic
94 profile is present, the 'default_bridge' profile found in
95 confbridge.conf is used. </para>
96 <para>It is important to note that while user profiles may be unique
97 for each participant, mixing bridge profiles on a single conference
98 is _NOT_ recommended and will produce undefined results.</para>
99 </parameter>
100 <parameter name="user_profile">
101 <para>The user profile name from confbridge.conf. When left blank,
102 a dynamically built user profile created by the CONFBRIDGE dialplan
103 function is searched for on the channel and used. If no dynamic
104 profile is present, the 'default_user' profile found in
105 confbridge.conf is used.</para>
106 </parameter>
107 <parameter name="menu">
108 <para>The name of the DTMF menu in confbridge.conf to be applied to
109 this channel. When left blank, a dynamically built menu profile
110 created by the CONFBRIDGE dialplan function is searched for on
111 the channel and used. If no dynamic profile is present, the
112 'default_menu' profile found in confbridge.conf is used.</para>
113 </parameter>
114 </syntax>
115 <description>
116 <para>Enters the user into a specified conference bridge. The user can
117 exit the conference by hangup or DTMF menu option.</para>
118 <para>This application sets the following channel variable upon completion:</para>
119 <variablelist>
120 <variable name="CONFBRIDGE_RESULT">
121 <value name="FAILED">The channel encountered an error and could not enter the conference.</value>
122 <value name="HANGUP">The channel exited the conference by hanging up.</value>
123 <value name="KICKED">The channel was kicked from the conference.</value>
124 <value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
125 <value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
126 <value name="TIMEOUT">The channel reached its configured timeout.</value>
127 </variable>
128 </variablelist>
129 </description>
130 <see-also>
131 <ref type="application">ConfKick</ref>
132 <ref type="function">CONFBRIDGE</ref>
133 <ref type="function">CONFBRIDGE_INFO</ref>
134 <ref type="function">CONFBRIDGE_CHANNELS</ref>
135 </see-also>
136 </application>
137 <application name="ConfKick" language="en_US">
138 <since>
139 <version>16.19.0</version>
140 <version>18.5.0</version>
141 </since>
142 <synopsis>
143 Kicks channel(s) from the requested ConfBridge.
144 </synopsis>
145 <syntax>
146 <parameter name="conference" required="true" />
147 <parameter name="channel">
148 <para>The channel to kick, <literal>all</literal>
149 to kick all users, or <literal>participants</literal>
150 to kick all non-admin participants. Default is all.</para>
151 </parameter>
152 </syntax>
153 <description>
154 <para>Kicks the requested channel(s) from a conference bridge.</para>
155 <variablelist>
156 <variable name="CONFKICKSTATUS">
157 <value name="FAILURE">
158 Could not kick any users with the provided arguments.
159 </value>
160 <value name="SUCCESS">
161 Successfully kicked users from specified conference bridge.
162 </value>
163 </variable>
164 </variablelist>
165 </description>
166 <see-also>
167 <ref type="application">ConfBridge</ref>
168 <ref type="function">CONFBRIDGE</ref>
169 <ref type="function">CONFBRIDGE_INFO</ref>
170 <ref type="function">CONFBRIDGE_CHANNELS</ref>
171 </see-also>
172 </application>
173 <function name="CONFBRIDGE" language="en_US">
174 <since>
175 <version>10.0.0</version>
176 </since>
177 <synopsis>
178 Set a custom dynamic bridge, user, or menu profile on a channel for the
179 ConfBridge application using the same options available in confbridge.conf.
180 </synopsis>
181 <syntax>
182 <parameter name="type" required="true">
183 <para>To what type of conference profile the option applies.</para>
184 <enumlist>
185 <enum name="bridge"></enum>
186 <enum name="menu"></enum>
187 <enum name="user"></enum>
188 </enumlist>
189 </parameter>
190 <parameter name="option" required="true">
191 <para>Option refers to a <filename>confbridge.conf</filename> option
192 that is being set dynamically on this channel, or <literal>clear</literal>
193 to remove already applied profile options from the channel.</para>
194 </parameter>
195 </syntax>
196 <description>
197 <para>A custom profile uses the default profile type settings defined in
198 <filename>confbridge.conf</filename> as defaults if the profile template
199 is not explicitly specified first.</para>
200 <para>For <literal>bridge</literal> profiles the default template is <literal>default_bridge</literal>.</para>
201 <para>For <literal>menu</literal> profiles the default template is <literal>default_menu</literal>.</para>
202 <para>For <literal>user</literal> profiles the default template is <literal>default_user</literal>.</para>
203 <para>---- Example 1 ----</para>
204 <para>In this example the custom user profile set on the channel will
205 automatically be used by the ConfBridge application.</para>
206 <example title="Example 1">
207 exten => 1,1,Answer()
208 </example>
209 <para>; In this example the effect of the following line is</para>
210 <para>; implied:</para>
211 <example title="Example 1b">
212 same => n,Set(CONFBRIDGE(user,template)=default_user)
213 same => n,Set(CONFBRIDGE(user,announce_join_leave)=yes)
214 same => n,Set(CONFBRIDGE(user,startmuted)=yes)
215 same => n,ConfBridge(1)
216 </example>
217 <para>---- Example 2 ----</para>
218 <para>This example shows how to use a predefined user profile in
219 <filename>confbridge.conf</filename> as a template for a dynamic profile.
220 Here we make an admin/marked user out of the <literal>my_user</literal>
221 profile that you define in <filename>confbridge.conf</filename>.</para>
222 <example title="Example 2">
223 exten => 1,1,Answer()
224 same => n,Set(CONFBRIDGE(user,template)=my_user)
225 same => n,Set(CONFBRIDGE(user,admin)=yes)
226 same => n,Set(CONFBRIDGE(user,marked)=yes)
227 same => n,ConfBridge(1)
228 </example>
229 </description>
230 </function>
231 <function name="CONFBRIDGE_INFO" language="en_US">
232 <since>
233 <version>10.0.0</version>
234 </since>
235 <synopsis>
236 Get information about a ConfBridge conference.
237 </synopsis>
238 <syntax>
239 <parameter name="type" required="true">
240 <para>What conference information is requested.</para>
241 <enumlist>
242 <enum name="admins">
243 <para>Get the number of admin users in the conference.</para>
244 </enum>
245 <enum name="locked">
246 <para>Determine if the conference is locked. (0 or 1)</para>
247 </enum>
248 <enum name="marked">
249 <para>Get the number of marked users in the conference.</para>
250 </enum>
251 <enum name="muted">
252 <para>Determine if the conference is muted. (0 or 1)</para>
253 </enum>
254 <enum name="parties">
255 <para>Get the number of users in the conference.</para>
256 </enum>
257 </enumlist>
258 </parameter>
259 <parameter name="conf" required="true">
260 <para>The name of the conference being referenced.</para>
261 </parameter>
262 </syntax>
263 <description>
264 <para>This function returns a non-negative integer for valid conference
265 names and an empty string for invalid conference names.</para>
266 </description>
267 <see-also>
268 <ref type="function">CONFBRIDGE_CHANNELS</ref>
269 </see-also>
270 </function>
271 <function name="CONFBRIDGE_CHANNELS" language="en_US">
272 <since>
273 <version>16.27.0</version>
274 <version>18.13.0</version>
275 <version>19.5.0</version>
276 </since>
277 <synopsis>
278 Get a list of channels in a ConfBridge conference.
279 </synopsis>
280 <syntax>
281 <parameter name="type" required="true">
282 <para>What conference information is requested.</para>
283 <enumlist>
284 <enum name="admins">
285 <para>Get the number of admin users in the conference.</para>
286 </enum>
287 <enum name="marked">
288 <para>Get the number of marked users in the conference.</para>
289 </enum>
290 <enum name="parties">
291 <para>Get the number of total users in the conference.</para>
292 </enum>
293 <enum name="active">
294 <para>Get the number of active users in the conference.</para>
295 </enum>
296 <enum name="waiting">
297 <para>Get the number of waiting users in the conference.</para>
298 </enum>
299 </enumlist>
300 </parameter>
301 <parameter name="conf" required="true">
302 <para>The name of the conference being referenced.</para>
303 </parameter>
304 </syntax>
305 <description>
306 <para>This function returns a comma-separated list of channels in a ConfBridge conference, optionally filtered by a type of participant.</para>
307 </description>
308 <see-also>
309 <ref type="function">CONFBRIDGE_INFO</ref>
310 </see-also>
311 </function>
312 <manager name="ConfbridgeList" language="en_US">
313 <since>
314 <version>10.0.0</version>
315 </since>
316 <synopsis>
317 List participants in a conference.
318 </synopsis>
319 <syntax>
320 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
321 <parameter name="Conference" required="true">
322 <para>Conference number.</para>
323 </parameter>
324 </syntax>
325 <description>
326 <para>Lists all users in a particular ConfBridge conference.
327 ConfbridgeList will follow as separate events, followed by a final event called
328 ConfbridgeListComplete.</para>
329 </description>
330 </manager>
331 <managerEvent language="en_US" name="ConfbridgeList">
332 <managerEventInstance class="EVENT_FLAG_REPORTING">
333 <since>
334 <version>13.20.0</version>
335 <version>15.3.0</version>
336 </since>
337 <synopsis>Raised as part of the ConfbridgeList action response list.</synopsis>
338 <syntax>
339 <parameter name="Conference">
340 <para>The name of the Confbridge conference.</para>
341 </parameter>
342 <parameter name="Admin">
343 <para>Identifies this user as an admin user.</para>
344 <enumlist>
345 <enum name="Yes"/>
346 <enum name="No"/>
347 </enumlist>
348 </parameter>
349 <parameter name="MarkedUser">
350 <para>Identifies this user as a marked user.</para>
351 <enumlist>
352 <enum name="Yes"/>
353 <enum name="No"/>
354 </enumlist>
355 </parameter>
356 <parameter name="WaitMarked">
357 <para>Must this user wait for a marked user to join?</para>
358 <enumlist>
359 <enum name="Yes"/>
360 <enum name="No"/>
361 </enumlist>
362 </parameter>
363 <parameter name="EndMarked">
364 <para>Does this user get kicked after the last marked user leaves?</para>
365 <enumlist>
366 <enum name="Yes"/>
367 <enum name="No"/>
368 </enumlist>
369 </parameter>
370 <parameter name="Waiting">
371 <para>Is this user waiting for a marked user to join?</para>
372 <enumlist>
373 <enum name="Yes"/>
374 <enum name="No"/>
375 </enumlist>
376 </parameter>
377 <parameter name="Muted">
378 <para>The current mute status.</para>
379 <enumlist>
380 <enum name="Yes"/>
381 <enum name="No"/>
382 </enumlist>
383 </parameter>
384 <parameter name="Talking">
385 <para>Is this user talking?</para>
386 <enumlist>
387 <enum name="Yes"/>
388 <enum name="No"/>
389 </enumlist>
390 </parameter>
391 <parameter name="AnsweredTime">
392 <para>The number of seconds the channel has been up.</para>
393 </parameter>
394 <channel_snapshot/>
395 </syntax>
396 </managerEventInstance>
397 </managerEvent>
398 <manager name="ConfbridgeListRooms" language="en_US">
399 <since>
400 <version>10.0.0</version>
401 </since>
402 <synopsis>
403 List active conferences.
404 </synopsis>
405 <syntax>
406 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
407 </syntax>
408 <description>
409 <para>Lists data about all active conferences.
410 ConfbridgeListRooms will follow as separate events, followed by a final event called
411 ConfbridgeListRoomsComplete.</para>
412 </description>
413 </manager>
414 <managerEvent language="en_US" name="ConfbridgeListRooms">
415 <managerEventInstance class="EVENT_FLAG_REPORTING">
416 <since>
417 <version>16.29.0</version>
418 <version>18.15.0</version>
419 <version>19.7.0</version>
420 </since>
421 <synopsis>Raised as part of the ConfbridgeListRooms action response list.</synopsis>
422 <syntax>
423 <parameter name="Conference">
424 <para>The name of the Confbridge conference.</para>
425 </parameter>
426 <parameter name="Parties">
427 <para>Number of users in the conference.</para>
428 <para>This includes both active and waiting users.</para>
429 </parameter>
430 <parameter name="Marked">
431 <para>Number of marked users in the conference.</para>
432 </parameter>
433 <parameter name="Locked">
434 <para>Is the conference locked?</para>
435 <enumlist>
436 <enum name="Yes"/>
437 <enum name="No"/>
438 </enumlist>
439 </parameter>
440 <parameter name="Muted">
441 <para>Is the conference muted?</para>
442 <enumlist>
443 <enum name="Yes"/>
444 <enum name="No"/>
445 </enumlist>
446 </parameter>
447 </syntax>
448 </managerEventInstance>
449 </managerEvent>
450 <manager name="ConfbridgeMute" language="en_US">
451 <since>
452 <version>10.0.0</version>
453 </since>
454 <synopsis>
455 Mute a Confbridge user.
456 </synopsis>
457 <syntax>
458 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
459 <parameter name="Conference" required="true" />
460 <parameter name="Channel" required="true">
461 <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
462 <para>If this parameter is "all", all channels will be muted.</para>
463 <para>If this parameter is "participants", all non-admin channels will be muted.</para>
464 </parameter>
465 </syntax>
466 <description>
467 </description>
468 </manager>
469 <manager name="ConfbridgeUnmute" language="en_US">
470 <since>
471 <version>10.0.0</version>
472 </since>
473 <synopsis>
474 Unmute a Confbridge user.
475 </synopsis>
476 <syntax>
477 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
478 <parameter name="Conference" required="true" />
479 <parameter name="Channel" required="true">
480 <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
481 <para>If this parameter is "all", all channels will be unmuted.</para>
482 <para>If this parameter is "participants", all non-admin channels will be unmuted.</para>
483 </parameter>
484 </syntax>
485 <description>
486 </description>
487 </manager>
488 <manager name="ConfbridgeKick" language="en_US">
489 <since>
490 <version>10.0.0</version>
491 </since>
492 <synopsis>
493 Kick a Confbridge user.
494 </synopsis>
495 <syntax>
496 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
497 <parameter name="Conference" required="true" />
498 <parameter name="Channel" required="true" >
499 <para>If this parameter is "all", all channels will be kicked from the conference.</para>
500 <para>If this parameter is "participants", all non-admin channels will be kicked from the conference.</para>
501 </parameter>
502 </syntax>
503 <description>
504 </description>
505 </manager>
506 <manager name="ConfbridgeLock" language="en_US">
507 <since>
508 <version>10.0.0</version>
509 </since>
510 <synopsis>
511 Lock a Confbridge conference.
512 </synopsis>
513 <syntax>
514 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
515 <parameter name="Conference" required="true" />
516 </syntax>
517 <description>
518 </description>
519 </manager>
520 <manager name="ConfbridgeUnlock" language="en_US">
521 <since>
522 <version>10.0.0</version>
523 </since>
524 <synopsis>
525 Unlock a Confbridge conference.
526 </synopsis>
527 <syntax>
528 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
529 <parameter name="Conference" required="true" />
530 </syntax>
531 <description>
532 </description>
533 </manager>
534 <manager name="ConfbridgeStartRecord" language="en_US">
535 <since>
536 <version>10.0.0</version>
537 </since>
538 <synopsis>
539 Start recording a Confbridge conference.
540 </synopsis>
541 <syntax>
542 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
543 <parameter name="Conference" required="true" />
544 <parameter name="RecordFile" required="false" />
545 </syntax>
546 <description>
547 <para>Start recording a conference. If recording is already present an error will be returned. If RecordFile is not provided, the default record file specified in the conference's bridge profile will be used, if that is not present either a file will automatically be generated in the monitor directory.</para>
548 </description>
549 </manager>
550 <manager name="ConfbridgeStopRecord" language="en_US">
551 <since>
552 <version>10.0.0</version>
553 </since>
554 <synopsis>
555 Stop recording a Confbridge conference.
556 </synopsis>
557 <syntax>
558 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
559 <parameter name="Conference" required="true" />
560 </syntax>
561 <description>
562 </description>
563 </manager>
564 <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
565 <since>
566 <version>10.0.0</version>
567 </since>
568 <synopsis>
569 Set a conference user as the single video source distributed to all other participants.
570 </synopsis>
571 <syntax>
572 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
573 <parameter name="Conference" required="true" />
574 <parameter name="Channel" required="true">
575 <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
576 </parameter>
577 </syntax>
578 <description>
579 </description>
580 </manager>
581
582***/
583
584/*!
585 * \par Playing back a file to a channel in a conference
586 * You might notice in this application that while playing a sound file
587 * to a channel the actual conference bridge lock is not held. This is done so
588 * that other channels are not blocked from interacting with the conference bridge.
589 * Unfortunately because of this it is possible for things to change after the sound file
590 * is done being played. Data must therefore be checked after reacquiring the conference
591 * bridge lock if it is important.
592 */
593
594static const char app[] = "ConfBridge";
595static const char app2[] = "ConfKick";
596
597/*! Number of buckets our conference bridges container can have */
598#define CONFERENCE_BRIDGE_BUCKETS 53
599
600/*! Initial recording filename space. */
601#define RECORD_FILENAME_INITIAL_SPACE 128
602
603/*! \brief Container to hold all conference bridges in progress */
605
606static void leave_conference(struct confbridge_user *user);
607static int play_sound_number(struct confbridge_conference *conference, int say_number);
608static int execute_menu_entry(struct confbridge_conference *conference,
609 struct confbridge_user *user,
610 struct ast_bridge_channel *bridge_channel,
611 struct conf_menu_entry *menu_entry,
612 struct conf_menu *menu);
613
614/*! \brief Hashing function used for conference bridges container */
615static int conference_bridge_hash_cb(const void *obj, const int flags)
616{
617 const struct confbridge_conference *conference = obj;
618 const char *name = obj;
619 int hash;
620
621 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
622 default:
623 case OBJ_POINTER:
624 name = conference->name;
625 /* Fall through */
626 case OBJ_KEY:
627 hash = ast_str_case_hash(name);
628 break;
629 case OBJ_PARTIAL_KEY:
630 /* Should never happen in hash callback. */
631 ast_assert(0);
632 hash = 0;
633 break;
634 }
635 return hash;
636}
637
638/*! \brief Comparison function used for conference bridges container */
639static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
640{
641 const struct confbridge_conference *left = obj;
642 const struct confbridge_conference *right = arg;
643 const char *right_name = arg;
644 int cmp;
645
646 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
647 default:
648 case OBJ_POINTER:
649 right_name = right->name;
650 /* Fall through */
651 case OBJ_KEY:
652 cmp = strcasecmp(left->name, right_name);
653 break;
654 case OBJ_PARTIAL_KEY:
655 cmp = strncasecmp(left->name, right_name, strlen(right_name));
656 break;
657 }
658 return cmp ? 0 : CMP_MATCH;
659}
660
661const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
662{
663 switch (sound) {
665 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
667 return S_OR(custom_sounds->hasleft, "conf-hasleft");
669 return S_OR(custom_sounds->kicked, "conf-kicked");
670 case CONF_SOUND_MUTED:
671 return S_OR(custom_sounds->muted, "conf-muted");
673 return S_OR(custom_sounds->unmuted, "conf-unmuted");
675 return S_OR(custom_sounds->binauralon, "confbridge-binaural-on");
677 return S_OR(custom_sounds->binauraloff, "confbridge-binaural-off");
679 return S_OR(custom_sounds->onlyone, "conf-onlyone");
681 return S_OR(custom_sounds->thereare, "conf-thereare");
683 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
685 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
687 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
689 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
691 return S_OR(custom_sounds->getpin, "conf-getpin");
693 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
695 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
697 return S_OR(custom_sounds->locked, "conf-locked");
699 return S_OR(custom_sounds->lockednow, "conf-lockednow");
701 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
703 return S_OR(custom_sounds->errormenu, "conf-errormenu");
704 case CONF_SOUND_JOIN:
705 return S_OR(custom_sounds->join, "confbridge-join");
706 case CONF_SOUND_LEAVE:
707 return S_OR(custom_sounds->leave, "confbridge-leave");
709 return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
711 return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
712 case CONF_SOUND_BEGIN:
713 return S_OR(custom_sounds->begin, "confbridge-conf-begin");
714 }
715
716 return "";
717}
718
719
720static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan,
721 struct stasis_message_type *type, struct ast_json *extras, int channel_topic)
722{
723 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
724 RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
725
726 json_object = ast_json_pack("{s: s}",
727 "conference", conference->name);
728 if (!json_object) {
729 return;
730 }
731
732 if (extras) {
733 ast_json_object_update(json_object, extras);
734 }
735
736 ast_bridge_lock(conference->bridge);
738 conference->bridge,
739 chan,
740 json_object);
741 ast_bridge_unlock(conference->bridge);
742 if (!msg) {
743 return;
744 }
745
747 conf_send_event_to_participants(conference, chan, msg);
748 }
749
750 if (channel_topic) {
752 } else {
753 stasis_publish(ast_bridge_topic(conference->bridge), msg);
754 }
755}
756
758 struct ast_channel_snapshot *chan_snapshot, struct stasis_message_type *type,
759 struct ast_json *extras)
760{
761 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
762 RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
763 RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup);
764
765 json_object = ast_json_pack("{s: s}",
766 "conference", conference->name);
767 if (!json_object) {
768 return;
769 }
770
771 if (extras) {
772 ast_json_object_update(json_object, extras);
773 }
774
775 ast_bridge_lock(conference->bridge);
776 bridge_snapshot = ast_bridge_snapshot_create(conference->bridge);
777 ast_bridge_unlock(conference->bridge);
778 if (!bridge_snapshot) {
779 return;
780 }
781
783 bridge_snapshot,
784 chan_snapshot,
785 json_object);
786 if (!msg) {
787 return;
788 }
789
790 stasis_publish(ast_bridge_topic(conference->bridge), msg);
791}
792
793
794static void send_conf_start_event(struct confbridge_conference *conference)
795{
797}
798
799static void send_conf_end_event(struct confbridge_conference *conference)
800{
801 send_conf_stasis(conference, NULL, confbridge_end_type(), NULL, 0);
802}
803
804static void send_join_event(struct confbridge_user *user, struct confbridge_conference *conference)
805{
806 struct ast_json *json_object;
807
808 json_object = ast_json_pack("{s: b, s: b}",
809 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN),
810 "muted", user->muted);
811 if (!json_object) {
812 return;
813 }
814 send_conf_stasis(conference, user->chan, confbridge_join_type(), json_object, 0);
815 ast_json_unref(json_object);
816}
817
818static void send_leave_event(struct confbridge_user *user, struct confbridge_conference *conference)
819{
820 struct ast_json *json_object;
821
822 json_object = ast_json_pack("{s: b}",
823 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
824 );
825 if (!json_object) {
826 return;
827 }
828 send_conf_stasis(conference, user->chan, confbridge_leave_type(), json_object, 0);
829 ast_json_unref(json_object);
830}
831
832static void send_start_record_event(struct confbridge_conference *conference)
833{
835}
836
837static void send_stop_record_event(struct confbridge_conference *conference)
838{
840}
841
842static void send_mute_event(struct confbridge_user *user, struct confbridge_conference *conference)
843{
844 struct ast_json *json_object;
845
846 json_object = ast_json_pack("{s: b}",
847 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
848 );
849 if (!json_object) {
850 return;
851 }
852 send_conf_stasis(conference, user->chan, confbridge_mute_type(), json_object, 1);
853 ast_json_unref(json_object);
854}
855
856static void send_unmute_event(struct confbridge_user *user, struct confbridge_conference *conference)
857{
858 struct ast_json *json_object;
859
860 json_object = ast_json_pack("{s: b}",
861 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
862 );
863 if (!json_object) {
864 return;
865 }
866 send_conf_stasis(conference, user->chan, confbridge_unmute_type(), json_object, 1);
867 ast_json_unref(json_object);
868}
869
870static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
871{
872 char *rec_file = conference->b_profile.rec_file;
873 char *ext;
874 time_t now;
875
876 if (ast_str_strlen(*filename)
878 && !is_new) {
879 return;
880 }
881
882 time(&now);
883
884 ast_str_reset(*filename);
885 if (ast_strlen_zero(rec_file)) {
886 ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name,
887 (unsigned int) now);
888 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_TIMESTAMP)) {
889 /* insert time before file extension */
890 ext = strrchr(rec_file, '.');
891 if (ext) {
892 ast_str_set_substr(filename, 0, rec_file, ext - rec_file);
893 ast_str_append(filename, 0, "-%u%s", (unsigned int) now, ext);
894 } else {
895 ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int) now);
896 }
897 } else {
898 ast_str_set(filename, 0, "%s", rec_file);
899 }
900 ast_str_append(filename, 0, ",%s%s,%s",
901 ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND) ? "a" : "",
902 conference->b_profile.rec_options,
903 conference->b_profile.rec_command);
904}
905
906static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
907{
908 if (!ast_strlen_zero(rec_file)) {
909 if (!*orig_rec_file) {
911 }
912
913 if (*orig_rec_file
914 && strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
915 ast_str_set(orig_rec_file, 0, "%s", rec_file);
916 return 1;
917 }
918 }
919 return 0;
920}
921
922struct confbridge_conference *conf_find_bridge(const char *conference_name)
923{
924 return ao2_find(conference_bridges, conference_name, OBJ_KEY);
925}
926
927/*!
928 * \internal
929 * \brief Returns whether or not conference is being recorded.
930 *
931 * \param conference The bridge to check for recording
932 *
933 * \note Must be called with the conference locked
934 *
935 * \retval 1 conference is recording.
936 * \retval 0 conference is NOT recording.
937 */
938static int conf_is_recording(struct confbridge_conference *conference)
939{
940 return conference->record_chan != NULL;
941}
942
943/*!
944 * \internal
945 * \brief Stop recording a conference bridge
946 *
947 * \param conference The conference bridge on which to stop the recording
948 *
949 * \note Must be called with the conference locked
950 *
951 * \retval -1 Failure
952 * \retval 0 Success
953 */
954static int conf_stop_record(struct confbridge_conference *conference)
955{
956 struct ast_channel *chan;
957 struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
958
959 if (!conf_is_recording(conference)) {
960 return -1;
961 }
962
963 /* Remove the recording channel from the conference bridge. */
964 chan = conference->record_chan;
965 conference->record_chan = NULL;
966 ast_queue_frame(chan, &f);
967 ast_channel_unref(chan);
968
969 ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
970 send_stop_record_event(conference);
971
972 return 0;
973}
974
975/*!
976 * \internal
977 * \brief Start recording the conference
978 *
979 * \param conference The conference bridge to start recording
980 *
981 * \note Must be called with the conference locked
982 *
983 * \retval 0 success
984 * \retval non-zero failure
985 */
986static int conf_start_record(struct confbridge_conference *conference)
987{
988 struct ast_app *mixmonapp;
989 struct ast_channel *chan;
990 struct ast_format_cap *cap;
991 struct ast_bridge_features *features;
992
993 if (conf_is_recording(conference)) {
994 return -1;
995 }
996
997 mixmonapp = pbx_findapp("MixMonitor");
998 if (!mixmonapp) {
999 ast_log(LOG_WARNING, "Cannot record ConfBridge, MixMonitor app is not installed\n");
1000 return -1;
1001 }
1002
1003 features = ast_bridge_features_new();
1004 if (!features) {
1005 return -1;
1006 }
1008
1010 if (!cap) {
1012 return -1;
1013 }
1015
1016 /* Create the recording channel. */
1017 chan = ast_request("CBRec", cap, NULL, NULL, conference->name, NULL);
1018 ao2_ref(cap, -1);
1019 if (!chan) {
1021 return -1;
1022 }
1023
1024 /* Start recording. */
1025 set_rec_filename(conference, &conference->record_filename,
1026 is_new_rec_file(conference->b_profile.rec_file, &conference->orig_rec_file));
1027 ast_answer(chan);
1028 pbx_exec(chan, mixmonapp, ast_str_buffer(conference->record_filename));
1029
1030 /* Put the channel into the conference bridge. */
1031 ast_channel_ref(chan);
1032 conference->record_chan = chan;
1033 if (ast_bridge_impart(conference->bridge, chan, NULL, features,
1035 ast_hangup(chan);
1036 ast_channel_unref(chan);
1037 conference->record_chan = NULL;
1038 return -1;
1039 }
1040
1041 ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
1042 send_start_record_event(conference);
1043
1044 return 0;
1045}
1046
1047/*! \brief Playback the given filename and monitor for any dtmf interrupts.
1048 *
1049 * This function is used to playback sound files on a given channel and optionally
1050 * allow dtmf interrupts to occur.
1051 *
1052 * If the optional bridge_channel parameter is given then sound file playback
1053 * is played on that channel and dtmf interruptions are allowed. However, if
1054 * bridge_channel is not set then the channel parameter is expected to be set
1055 * instead and non interruptible playback is played on that channel.
1056 *
1057 * \param bridge_channel Bridge channel to play file on
1058 * \param channel Optional channel to play file on if bridge_channel not given
1059 * \param filename The file name to playback
1060 *
1061 * \retval -1 failure during playback.
1062 * \retval 0 on file was fully played.
1063 * \retval 1 on dtmf interrupt.
1064 */
1065static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel,
1066 const char *filename)
1067{
1068 struct ast_channel *chan;
1069 const char *stop_digits;
1070 int digit;
1071
1072 if (bridge_channel) {
1073 chan = bridge_channel->chan;
1074 stop_digits = AST_DIGIT_ANY;
1075 } else {
1076 chan = channel;
1077 stop_digits = AST_DIGIT_NONE;
1078 }
1079
1080 digit = ast_stream_and_wait(chan, filename, stop_digits);
1081 if (digit < 0) {
1082 ast_log(LOG_WARNING, "Failed to playback file '%s' to channel\n", filename);
1083 return -1;
1084 }
1085
1086 if (digit > 0) {
1089 return 1;
1090 }
1091
1092 return 0;
1093}
1094
1095/*!
1096 * \internal
1097 * \brief Complain if the given sound file does not exist.
1098 *
1099 * \param filename Sound file to check if exists.
1100 *
1101 * \retval non-zero if the file exists.
1102 */
1103static int sound_file_exists(const char *filename)
1104{
1105 if (ast_fileexists(filename, NULL, NULL)) {
1106 return -1;
1107 }
1108 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
1109 return 0;
1110}
1111
1112/*!
1113 * \brief Announce number of users in the conference bridge to the caller
1114 *
1115 * \param conference Conference bridge to peek at
1116 * \param user Optional Caller
1117 * \param bridge_channel The bridged channel involved
1118 *
1119 * \note if caller is NULL, the announcement will be sent to all participants in the conference.
1120 * \retval 0 on success.
1121 * \retval -1 if the user hung up.
1122 */
1123static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user,
1125{
1126 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference->b_profile.sounds);
1127 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference->b_profile.sounds);
1128 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference->b_profile.sounds);
1129
1130 if (conference->activeusers <= 1) {
1131 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
1132 return 0;
1133 } else if (conference->activeusers == 2) {
1134 if (user) {
1135 /* Eep, there is one other person */
1136 if (play_file(bridge_channel, user->chan, only_one) < 0) {
1137 return -1;
1138 }
1139 } else {
1140 play_sound_file(conference, only_one);
1141 }
1142 } else {
1143 /* Alas multiple others in here */
1144 if (user) {
1145 if (ast_stream_and_wait(user->chan,
1146 there_are,
1147 "")) {
1148 return -1;
1149 }
1150 if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) {
1151 return -1;
1152 }
1153 if (play_file(bridge_channel, user->chan, other_in_party) < 0) {
1154 return -1;
1155 }
1156 } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
1157 play_sound_file(conference, there_are);
1158 play_sound_number(conference, conference->activeusers - 1);
1159 play_sound_file(conference, other_in_party);
1160 }
1161 }
1162 return 0;
1163}
1164
1165/*!
1166 * \brief Play back an audio file to a channel
1167 *
1168 * \param user User to play audio prompt to
1169 * \param filename Prompt to play
1170 *
1171 * \retval 0 on success.
1172 * \retval -1 if the user hung up.
1173 *
1174 * \note Generally this should be called when the conference is unlocked to avoid blocking
1175 * the entire conference while the sound is played. But don't unlock the conference bridge
1176 * in the middle of a state transition.
1177 */
1178static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
1179{
1180 return ast_stream_and_wait(user->chan, filename, "");
1181}
1182
1183static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked)
1184{
1185 /* Right now, only marked users are automatically set as the single src of video.*/
1186 if (!marked) {
1187 return;
1188 }
1189
1191 int set = 1;
1192 struct confbridge_user *user = NULL;
1193
1195 /* see if anyone is already the video src */
1197 if (user->chan == chan) {
1198 continue;
1199 }
1201 set = 0;
1202 break;
1203 }
1204 }
1206 if (set) {
1208 }
1210 /* we joined and are video capable, we override anyone else that may have already been the video feed */
1212 }
1213}
1214
1216{
1217 struct confbridge_user *user = NULL;
1218
1219 /* if this isn't a video source, nothing to update */
1221 return;
1222 }
1223
1225
1226 /* If in follow talker mode, make sure to restore this mode on the
1227 * bridge when a source is removed. It is possible this channel was
1228 * only set temporarily as a video source by an AMI or DTMF action. */
1231 }
1232
1233 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
1236 return;
1237 }
1238
1239 /* Make the next available marked user the video src. */
1242 if (user->chan == chan) {
1243 continue;
1244 }
1245 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1247 break;
1248 }
1249 }
1251}
1252
1254{
1259};
1260
1261/*!
1262 * \brief Hang up the announcer channel
1263 *
1264 * This hangs up the announcer channel in the conference. This
1265 * runs in the playback queue taskprocessor since we do not want
1266 * to hang up the channel while it's trying to play an announcement.
1267 *
1268 * This task is performed synchronously, so there is no need to
1269 * perform any cleanup on the passed-in data.
1270 *
1271 * \param data A hangup_data structure
1272 * \return 0
1273 */
1274static int hangup_playback(void *data)
1275{
1276 struct hangup_data *hangup = data;
1277
1278 ast_autoservice_stop(hangup->conference->playback_chan);
1279
1280 ast_hangup(hangup->conference->playback_chan);
1281 hangup->conference->playback_chan = NULL;
1282
1283 ast_mutex_lock(&hangup->lock);
1284 hangup->hungup = 1;
1285 ast_cond_signal(&hangup->cond);
1286 ast_mutex_unlock(&hangup->lock);
1287
1288 return 0;
1289}
1290
1292{
1293 ast_mutex_init(&hangup->lock);
1294 ast_cond_init(&hangup->cond, NULL);
1295
1296 hangup->conference = conference;
1297 hangup->hungup = 0;
1298}
1299
1301{
1302 ast_mutex_destroy(&hangup->lock);
1303 ast_cond_destroy(&hangup->cond);
1304}
1305
1306/*!
1307 * \brief Destroy a conference bridge
1308 *
1309 * \param obj The conference bridge object
1310 */
1311static void destroy_conference_bridge(void *obj)
1312{
1313 struct confbridge_conference *conference = obj;
1314
1315 ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
1316
1317 if (conference->playback_chan) {
1318 if (conference->playback_queue) {
1319 struct hangup_data hangup;
1321
1323 ast_mutex_lock(&hangup.lock);
1324 while (!hangup.hungup) {
1325 ast_cond_wait(&hangup.cond, &hangup.lock);
1326 }
1327 ast_mutex_unlock(&hangup.lock);
1328 }
1329
1331 } else {
1332 /* Playback queue is not yet allocated. Just hang up the channel straight */
1335 }
1336 }
1337
1338 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
1339 if (conference->bridge) {
1342 }
1343
1347
1350}
1351
1352/*! \brief Call the proper join event handler for the user for the conference bridge's current state
1353 * \internal
1354 * \param user The conference bridge user that is joining
1355 * \retval 0 success
1356 * \retval -1 failure
1357 */
1359{
1361 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1362 handler = user->conference->state->join_marked;
1363 } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
1364 handler = user->conference->state->join_waitmarked;
1365 } else {
1366 handler = user->conference->state->join_unmarked;
1367 }
1368
1370
1371 if (!handler) {
1373 return -1;
1374 }
1375
1376 handler(user);
1377
1378 return 0;
1379}
1380
1381/*! \brief Call the proper leave event handler for the user for the conference bridge's current state
1382 * \internal
1383 * \param user The conference bridge user that is leaving
1384 * \retval 0 success
1385 * \retval -1 failure
1386 */
1388{
1390 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1391 handler = user->conference->state->leave_marked;
1392 } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
1393 handler = user->conference->state->leave_waitmarked;
1394 } else {
1395 handler = user->conference->state->leave_unmarked;
1396 }
1397
1399
1400 if (!handler) {
1401 /* This should never happen. If it does, though, it is bad. The user will not have been removed
1402 * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
1403 * Shouldn't happen, though. */
1405 return -1;
1406 }
1407
1408 handler(user);
1409
1410 return 0;
1411}
1412
1414{
1415 int mute_user;
1416 int mute_system;
1417 int mute_effective;
1418
1419 /* User level mute request. */
1420 mute_user = user->muted;
1421
1422 /* System level mute request. */
1423 mute_system = user->playing_moh
1424 /*
1425 * Do not allow waitmarked users to talk to anyone unless there
1426 * is a marked user present.
1427 */
1428 || (!user->conference->markedusers
1429 && ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED));
1430
1431 mute_effective = mute_user || mute_system;
1432
1433 ast_debug(1, "User %s is %s: user:%d system:%d.\n",
1434 ast_channel_name(user->chan), mute_effective ? "muted" : "unmuted",
1435 mute_user, mute_system);
1436 user->features.mute = mute_effective;
1437 ast_test_suite_event_notify("CONF_MUTE_UPDATE",
1438 "Mode: %s\r\n"
1439 "Conference: %s\r\n"
1440 "Channel: %s",
1441 mute_effective ? "muted" : "unmuted",
1442 user->conference->b_profile.name,
1443 ast_channel_name(user->chan));
1444}
1445
1446/*!
1447 * \internal
1448 * \brief Mute/unmute a single user.
1449 */
1451{
1452 /* Set user level mute request. */
1453 user->muted = mute ? 1 : 0;
1454
1456 ast_test_suite_event_notify("CONF_MUTE",
1457 "Message: participant %s %s\r\n"
1458 "Conference: %s\r\n"
1459 "Channel: %s",
1460 ast_channel_name(user->chan),
1461 mute ? "muted" : "unmuted",
1463 ast_channel_name(user->chan));
1464 if (mute) {
1466 } else {
1468 }
1469}
1470
1472{
1473 user->playing_moh = 0;
1474 if (!user->suspended_moh) {
1475 int in_bridge;
1476
1477 /*
1478 * Locking the ast_bridge here is the only way to hold off the
1479 * call to ast_bridge_join() in confbridge_exec() from
1480 * interfering with the bridge and MOH operations here.
1481 */
1482 ast_bridge_lock(user->conference->bridge);
1483
1484 /*
1485 * Temporarily suspend the user from the bridge so we have
1486 * control to stop MOH if needed.
1487 */
1488 in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1489 ast_moh_stop(user->chan);
1490 if (in_bridge) {
1491 ast_bridge_unsuspend(user->conference->bridge, user->chan);
1492 }
1493
1494 ast_bridge_unlock(user->conference->bridge);
1495 }
1496}
1497
1499{
1500 user->playing_moh = 1;
1501 if (!user->suspended_moh) {
1502 int in_bridge;
1503
1504 /*
1505 * Locking the ast_bridge here is the only way to hold off the
1506 * call to ast_bridge_join() in confbridge_exec() from
1507 * interfering with the bridge and MOH operations here.
1508 */
1509 ast_bridge_lock(user->conference->bridge);
1510
1511 /*
1512 * Temporarily suspend the user from the bridge so we have
1513 * control to start MOH if needed.
1514 */
1515 in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1516 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1517 if (in_bridge) {
1518 ast_bridge_unsuspend(user->conference->bridge, user->chan);
1519 }
1520
1521 ast_bridge_unlock(user->conference->bridge);
1522 }
1523}
1524
1525/*!
1526 * \internal
1527 * \brief Unsuspend MOH for the conference user.
1528 *
1529 * \param user Conference user to unsuspend MOH on.
1530 */
1532{
1533 ao2_lock(user->conference);
1534 if (--user->suspended_moh == 0 && user->playing_moh) {
1535 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1536 }
1537 ao2_unlock(user->conference);
1538}
1539
1540/*!
1541 * \internal
1542 * \brief Suspend MOH for the conference user.
1543 *
1544 * \param user Conference user to suspend MOH on.
1545 */
1547{
1548 ao2_lock(user->conference);
1549 if (user->suspended_moh++ == 0 && user->playing_moh) {
1550 ast_moh_stop(user->chan);
1551 }
1552 ao2_unlock(user->conference);
1553}
1554
1556{
1557 /* If we have not been quieted play back that they are waiting for the leader */
1559 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, user->conference->b_profile.sounds))) {
1560 /* user hungup while the sound was playing */
1561 return -1;
1562 }
1563 return 0;
1564}
1565
1567{
1568 /* If audio prompts have not been quieted or this prompt quieted play it on out */
1571 conf_get_sound(CONF_SOUND_ONLY_PERSON, user->conference->b_profile.sounds))) {
1572 /* user hungup while the sound was playing */
1573 return -1;
1574 }
1575 }
1576 return 0;
1577}
1578
1580{
1581 struct post_join_action *action;
1582 if (!(action = ast_calloc(1, sizeof(*action)))) {
1583 return -1;
1584 }
1585 action->func = func;
1586 AST_LIST_INSERT_TAIL(&user->post_join_list, action, list);
1587 return 0;
1588}
1589
1590
1592{
1593 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference->name);
1594}
1595
1597{
1598 /* If we are the second participant we may need to stop music on hold on the first */
1599 struct confbridge_user *first_user = AST_LIST_FIRST(&conference->active_list);
1600
1601 if (ast_test_flag(&first_user->u_profile, USER_OPT_MUSICONHOLD)) {
1602 conf_moh_stop(first_user);
1603 }
1604 conf_update_user_mute(first_user);
1605}
1606
1608{
1609 struct pbx_find_info q = { .stacklen = 0 };
1610
1611 /* Called with a reference to conference */
1612 ao2_unlink(conference_bridges, conference);
1613 send_conf_end_event(conference);
1614 if (!ast_strlen_zero(conference->b_profile.regcontext) &&
1616 conference->name, 1, NULL, "", E_MATCH)) {
1618 conference->name, 1, NULL);
1619 }
1620 ao2_lock(conference);
1621 conf_stop_record(conference);
1622 ao2_unlock(conference);
1623}
1624
1625/*!
1626 * \internal
1627 * \brief Allocate playback channel for a conference.
1628 * \pre expects conference to be locked before calling this function
1629 */
1630static int alloc_playback_chan(struct confbridge_conference *conference)
1631{
1632 struct ast_format_cap *cap;
1633 char taskprocessor_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1634
1636 if (!cap) {
1637 return -1;
1638 }
1640 conference->playback_chan = ast_request("CBAnn", cap, NULL, NULL,
1641 conference->name, NULL);
1642 ao2_ref(cap, -1);
1643 if (!conference->playback_chan) {
1644 return -1;
1645 }
1646
1647 /* To make sure playback_chan has the same language as the bridge */
1648 ast_channel_lock(conference->playback_chan);
1649 ast_channel_language_set(conference->playback_chan, conference->b_profile.language);
1650 ast_channel_unlock(conference->playback_chan);
1651
1652 ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
1653 ast_channel_name(conference->playback_chan), conference->name);
1654
1655 ast_taskprocessor_build_name(taskprocessor_name, sizeof(taskprocessor_name),
1656 "Confbridge/%s", conference->name);
1657 conference->playback_queue = ast_taskprocessor_get(taskprocessor_name, TPS_REF_DEFAULT);
1658 if (!conference->playback_queue) {
1659 ast_hangup(conference->playback_chan);
1660 conference->playback_chan = NULL;
1661 return -1;
1662 }
1663 return 0;
1664}
1665
1666/*!
1667 * \brief Push the announcer channel into the bridge
1668 *
1669 * \param conference Conference bridge to push the announcer to
1670 * \retval 0 Success
1671 * \retval -1 Failed to push the channel to the bridge
1672 */
1673static int push_announcer(struct confbridge_conference *conference)
1674{
1675 if (conf_announce_channel_push(conference->playback_chan)) {
1676 ast_hangup(conference->playback_chan);
1677 conference->playback_chan = NULL;
1678 return -1;
1679 }
1680
1682 return 0;
1683}
1684
1685static void confbridge_unlock_and_unref(void *obj)
1686{
1687 struct confbridge_conference *conference = obj;
1688
1689 if (!obj) {
1690 return;
1691 }
1692 ao2_unlock(conference);
1693 ao2_ref(conference, -1);
1694}
1695
1697{
1698 struct ast_channel_snapshot *old_snapshot;
1699 struct ast_channel_snapshot *new_snapshot;
1700 char *confbr_name = NULL;
1701 char *comma;
1703 struct confbridge_user *user = NULL;
1704 int found_user = 0;
1705 struct ast_json *json_object;
1706
1708 && strcmp(msg->to_transferee.channel_snapshot->dialplan->appl, "ConfBridge") == 0
1709 && msg->target) {
1710 /* We're transferring a bridge to an extension */
1711 old_snapshot = msg->to_transferee.channel_snapshot;
1712 new_snapshot = msg->target;
1713 } else if (msg->to_transfer_target.channel_snapshot
1714 && strcmp(msg->to_transfer_target.channel_snapshot->dialplan->appl, "ConfBridge") == 0
1715 && msg->transferee) {
1716 /* We're transferring a call to a bridge */
1717 old_snapshot = msg->to_transfer_target.channel_snapshot;
1718 new_snapshot = msg->transferee;
1719 } else {
1720 ast_log(LOG_ERROR, "Could not determine proper channels\n");
1721 return;
1722 }
1723
1724 /*
1725 * old_snapshot->data should have the original parameters passed to
1726 * the ConfBridge app:
1727 * conference[,bridge_profile[,user_profile[,menu]]]
1728 * We'll use "conference" to look up the bridge.
1729 *
1730 * We _could_ use old_snapshot->bridgeid to get the bridge but
1731 * that would involve locking the conference_bridges container
1732 * and iterating over it looking for a matching bridge.
1733 */
1734 if (ast_strlen_zero(old_snapshot->dialplan->data)) {
1735 ast_log(LOG_ERROR, "Channel '%s' didn't have app data set\n", old_snapshot->base->name);
1736 return;
1737 }
1738 confbr_name = ast_strdupa(old_snapshot->dialplan->data);
1739 comma = strchr(confbr_name, ',');
1740 if (comma) {
1741 *comma = '\0';
1742 }
1743
1744 ast_debug(1, "Confbr: %s Leaving: %s Joining: %s\n", confbr_name, old_snapshot->base->name, new_snapshot->base->name);
1745
1746 conference = ao2_find(conference_bridges, confbr_name, OBJ_SEARCH_KEY);
1747 if (!conference) {
1748 ast_log(LOG_ERROR, "Conference bridge '%s' not found\n", confbr_name);
1749 return;
1750 }
1751 ao2_lock(conference);
1752
1753 /*
1754 * We need to grab the user profile for the departing user in order to
1755 * properly format the join/leave messages.
1756 */
1757 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
1758 if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
1759 found_user = 1;
1760 break;
1761 }
1762 }
1763
1764 /*
1765 * If we didn't find the user in the active list, try the waiting list.
1766 */
1767 if (!found_user && conference->waitingusers) {
1768 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
1769 if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
1770 found_user = 1;
1771 break;
1772 }
1773 }
1774 }
1775
1776 if (!found_user) {
1777 ast_log(LOG_ERROR, "Unable to find user profile for channel '%s' in bridge '%s'\n",
1778 old_snapshot->base->name, confbr_name);
1779 return;
1780 }
1781
1782 /*
1783 * We're going to use the existing user profile to create the messages.
1784 */
1785 json_object = ast_json_pack("{s: b}",
1786 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
1787 );
1788 if (!json_object) {
1789 return;
1790 }
1791
1792 send_conf_stasis_snapshots(conference, old_snapshot, confbridge_leave_type(), json_object);
1793 ast_json_unref(json_object);
1794
1795 json_object = ast_json_pack("{s: b, s: b}",
1796 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN),
1797 "muted", user->muted);
1798 if (!json_object) {
1799 return;
1800 }
1801 send_conf_stasis_snapshots(conference, new_snapshot, confbridge_join_type(), json_object);
1802 ast_json_unref(json_object);
1803}
1804
1805/*!
1806 * \brief Join a conference bridge
1807 *
1808 * \param conference_name The conference name
1809 * \param user Conference bridge user structure
1810 *
1811 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1812 */
1813static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user)
1814{
1815 struct confbridge_conference *conference;
1816 struct post_join_action *action;
1817 int max_members_reached = 0;
1818
1819 /* We explicitly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same time */
1821
1822 ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name);
1823
1824 /* Attempt to find an existing conference bridge */
1825 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
1826 if (conference && conference->b_profile.max_members) {
1827 max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1;
1828 }
1829
1830 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1831 if (conference && (max_members_reached || conference->locked) && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
1833 ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", conference_name);
1836 "");
1837 ao2_ref(conference, -1);
1838 return NULL;
1839 }
1840
1841 /* If no conference bridge was found see if we can create one */
1842 if (!conference) {
1843 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1844 if (!(conference = ao2_alloc(sizeof(*conference), destroy_conference_bridge))) {
1846 ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", conference_name);
1847 return NULL;
1848 }
1849
1850 /* Setup for the record channel */
1852 if (!conference->record_filename) {
1853 ao2_ref(conference, -1);
1855 return NULL;
1856 }
1857
1858 /* Setup conference bridge parameters */
1859 ast_copy_string(conference->name, conference_name, sizeof(conference->name));
1860 conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
1861
1862 /* Create an actual bridge that will do the audio mixing */
1865 app, conference_name, NULL);
1866 if (!conference->bridge) {
1867 ao2_ref(conference, -1);
1869 ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name);
1870 return NULL;
1871 }
1872
1873 /* Set the internal sample rate on the bridge from the bridge profile */
1875 /* Set the maximum sample rate on the bridge from the bridge profile */
1877 /* Set the internal mixing interval on the bridge from the bridge profile */
1880
1883 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
1888 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST)) {
1890 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
1898 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_FORCE)) {
1901 }
1902 }
1903
1904 /* Always set the minimum interval between video updates, to avoid infinite video updates. */
1906
1908 ast_bridge_set_send_sdp_label(conference->bridge, 1);
1909 }
1910
1911 /* Link it into the conference bridges container */
1912 if (!ao2_link(conference_bridges, conference)) {
1913 ao2_ref(conference, -1);
1916 "Conference '%s' could not be added to the conferences list.\n", conference_name);
1917 return NULL;
1918 }
1919
1920 /* Set the initial state to EMPTY */
1921 conference->state = CONF_STATE_EMPTY;
1922
1923 if (alloc_playback_chan(conference)) {
1924 ao2_unlink(conference_bridges, conference);
1925 ao2_ref(conference, -1);
1927 ast_log(LOG_ERROR, "Could not allocate announcer channel for conference '%s'\n", conference_name);
1928 return NULL;
1929 }
1930
1931 if (push_announcer(conference)) {
1932 ao2_unlink(conference_bridges, conference);
1933 ao2_ref(conference, -1);
1935 ast_log(LOG_ERROR, "Could not add announcer channel for conference '%s' bridge\n", conference_name);
1936 return NULL;
1937 }
1938
1940 ao2_lock(conference);
1941 conf_start_record(conference);
1942 ao2_unlock(conference);
1943 }
1944
1945 send_conf_start_event(conference);
1946
1947 if (!ast_strlen_zero(conference->b_profile.regcontext)) {
1948 if (!ast_exists_extension(NULL, conference->b_profile.regcontext, conference->name, 1, NULL)) {
1949 ast_add_extension(conference->b_profile.regcontext, 1, conference->name, 1, NULL, NULL, "Noop", NULL, NULL, "ConfBridge");
1950 }
1951 }
1952
1953 ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);
1954 }
1955
1957
1958 /* Setup conference bridge user parameters */
1959 user->conference = conference;
1960
1961 ao2_lock(conference);
1962
1963 /* Determine if the new user should join the conference muted. */
1964 if (ast_test_flag(&user->u_profile, USER_OPT_STARTMUTED)
1965 || (!ast_test_flag(&user->u_profile, USER_OPT_ADMIN) && conference->muted)) {
1966 /* Set user level mute request. */
1967 user->muted = 1;
1968 }
1969
1970 /*
1971 * Suspend any MOH until the user actually joins the bridge of
1972 * the conference. This way any pre-join file playback does not
1973 * need to worry about MOH.
1974 */
1975 user->suspended_moh = 1;
1976
1978 /* Invalid event, nothing was done, so we don't want to process a leave. */
1979 ao2_unlock(conference);
1980 ao2_ref(conference, -1);
1981 user->conference = NULL;
1982 return NULL;
1983 }
1984
1985 if (ast_check_hangup(user->chan)) {
1986 ao2_unlock(conference);
1988 return NULL;
1989 }
1990
1991 ao2_unlock(conference);
1992
1993 /* If an announcement is to be played play it */
1994 if (!ast_strlen_zero(user->u_profile.announcement)) {
1996 user->u_profile.announcement)) {
1998 return NULL;
1999 }
2000 }
2001
2002 /* Announce number of users if need be */
2003 if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
2004 if (announce_user_count(conference, user, NULL)) {
2006 return NULL;
2007 }
2008 }
2009
2011 (conference->activeusers > user->u_profile.announce_user_count_all_after)) {
2012 int user_count_res;
2013
2014 /*
2015 * We have to autoservice the new user because he has not quite
2016 * joined the conference yet.
2017 */
2019 user_count_res = announce_user_count(conference, NULL, NULL);
2021 if (user_count_res) {
2023 return NULL;
2024 }
2025 }
2026
2027 /* Handle post-join actions */
2028 while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
2029 action->func(user);
2030 ast_free(action);
2031 }
2032
2033 return conference;
2034}
2035
2036/*!
2037 * \brief Leave a conference
2038 *
2039 * \param user The conference user
2040 */
2042{
2043 struct post_join_action *action;
2044
2045 ao2_lock(user->conference);
2047 ao2_unlock(user->conference);
2048
2049 /* Discard any post-join actions */
2050 while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
2051 ast_free(action);
2052 }
2053
2054 /* Done mucking with the conference, huzzah */
2055 ao2_ref(user->conference, -1);
2056 user->conference = NULL;
2057}
2058
2059static void playback_common(struct confbridge_conference *conference, const char *filename, int say_number)
2060{
2061 /* Don't try to play if the playback channel has been hung up */
2062 if (!conference->playback_chan) {
2063 return;
2064 }
2065
2067
2068 /* The channel is all under our control, in goes the prompt */
2069 if (!ast_strlen_zero(filename)) {
2070 ast_stream_and_wait(conference->playback_chan, filename, "");
2071 } else if (say_number >= 0) {
2072 ast_say_number(conference->playback_chan, say_number, "",
2074 }
2075
2077}
2078
2081 const char *filename;
2086};
2087
2088/*!
2089 * \brief Play an announcement into a confbridge
2090 *
2091 * This runs in the playback queue taskprocessor. This ensures that
2092 * all playbacks are handled in sequence and do not play over top one
2093 * another.
2094 *
2095 * This task runs synchronously so there is no need for performing any
2096 * sort of cleanup on the input parameter.
2097 *
2098 * \param data A playback_task_data
2099 * \return 0
2100 */
2101static int playback_task(void *data)
2102{
2103 struct playback_task_data *ptd = data;
2104
2106
2107 ast_mutex_lock(&ptd->lock);
2108 ptd->playback_finished = 1;
2109 ast_cond_signal(&ptd->cond);
2110 ast_mutex_unlock(&ptd->lock);
2111
2112 return 0;
2113}
2114
2116 const char *filename, int say_number)
2117{
2118 ast_mutex_init(&ptd->lock);
2119 ast_cond_init(&ptd->cond, NULL);
2120
2121 ptd->filename = filename;
2122 ptd->say_number = say_number;
2123 ptd->conference = conference;
2124 ptd->playback_finished = 0;
2125}
2126
2128{
2129 ast_mutex_destroy(&ptd->lock);
2130 ast_cond_destroy(&ptd->cond);
2131}
2132
2134{
2135 struct playback_task_data ptd;
2136
2137 /* Do not waste resources trying to play files that do not exist */
2139 if (say_number < 0) {
2140 return 0;
2141 }
2142 } else if (!sound_file_exists(filename)) {
2143 return 0;
2144 }
2145
2148 if (!ast_strlen_zero(filename)) {
2149 ast_log(LOG_WARNING, "Unable to play file '%s' to conference %s\n",
2151 } else {
2152 ast_log(LOG_WARNING, "Unable to say number '%d' to conference %s\n",
2154 }
2156 return -1;
2157 }
2158
2159 /* Wait for the playback to complete */
2160 ast_mutex_lock(&ptd.lock);
2161 while (!ptd.playback_finished) {
2162 ast_cond_wait(&ptd.cond, &ptd.lock);
2163 }
2164 ast_mutex_unlock(&ptd.lock);
2165
2167
2168 return 0;
2169}
2170
2172{
2174}
2175
2180 char filename[0];
2181};
2182
2186 int wait;
2187};
2188
2189static void async_datastore_data_destroy(void *data)
2190{
2191 struct async_datastore_data *add = data;
2192
2193 ast_mutex_destroy(&add->lock);
2194 ast_cond_destroy(&add->cond);
2195
2196 ast_free(add);
2197}
2198
2199/*!
2200 * \brief Datastore used for timing of async announcement playback
2201 *
2202 * Announcements that are played to the entire conference can be played
2203 * asynchronously (i.e. The channel that queues the playback does not wait
2204 * for the playback to complete before continuing)
2205 *
2206 * The thing about async announcements is that the channel that queues the
2207 * announcement is either not in the bridge or is in some other way "occupied"
2208 * at the time the announcement is queued. Because of that, the initiator of
2209 * the announcement may enter after the announcement has already started,
2210 * resulting in the sound being "clipped".
2211 *
2212 * This datastore makes it so that the channel that queues the async announcement
2213 * can say "I'm ready now". This way the announcement does not start until the
2214 * initiator of the announcement is ready to hear the sound.
2215 */
2217 .type = "Confbridge async playback",
2219};
2220
2222{
2223 struct async_datastore_data *add;
2224
2225 add = ast_malloc(sizeof(*add));
2226 if (!add) {
2227 return NULL;
2228 }
2229
2230 ast_mutex_init(&add->lock);
2231 ast_cond_init(&add->cond, NULL);
2232 add->wait = 1;
2233
2234 return add;
2235}
2236
2237/*!
2238 * \brief Prepare the async playback datastore
2239 *
2240 * This is done prior to queuing an async announcement. If the
2241 * datastore has not yet been created, it is allocated and initialized.
2242 * If it already exists, we set it to be in "waiting" mode.
2243 *
2244 * \param initiator The channel that is queuing the async playback
2245 * \retval 0 Success
2246 * \retval -1 Failure :(
2247 */
2248static int setup_async_playback_datastore(struct ast_channel *initiator)
2249{
2250 struct ast_datastore *async_datastore;
2251
2252 async_datastore = ast_channel_datastore_find(initiator, &async_datastore_info, NULL);
2253 if (async_datastore) {
2254 struct async_datastore_data *add;
2255
2256 add = async_datastore->data;
2257 add->wait = 1;
2258
2259 return 0;
2260 }
2261
2262 async_datastore = ast_datastore_alloc(&async_datastore_info, NULL);
2263 if (!async_datastore) {
2264 return -1;
2265 }
2266
2267 async_datastore->data = async_datastore_data_alloc();
2268 if (!async_datastore->data) {
2269 ast_datastore_free(async_datastore);
2270 return -1;
2271 }
2272
2273 ast_channel_datastore_add(initiator, async_datastore);
2274 return 0;
2275}
2276
2278 struct confbridge_conference *conference, const char *filename, int say_number,
2279 struct ast_channel *initiator)
2280{
2281 struct async_playback_task_data *aptd;
2282
2283 aptd = ast_malloc(sizeof(*aptd) + strlen(filename) + 1);
2284 if (!aptd) {
2285 return NULL;
2286 }
2287
2288 /* Safe */
2289 strcpy(aptd->filename, filename);
2290 aptd->say_number = say_number;
2291
2292 /* You may think that we need to bump the conference refcount since we are pushing
2293 * this task to the taskprocessor.
2294 *
2295 * In this case, that actually causes a problem. The destructor for the conference
2296 * pushes a hangup task into the taskprocessor and waits for it to complete before
2297 * continuing. If the destructor gets called from a taskprocessor task, we're
2298 * deadlocked.
2299 *
2300 * So is there a risk of the conference being freed out from under us? No. Since
2301 * the destructor pushes a task into the taskprocessor and waits for it to complete,
2302 * the destructor cannot free the conference out from under us. No further tasks
2303 * can be queued onto the taskprocessor after the hangup since no channels are referencing
2304 * the conference at that point any more.
2305 */
2306 aptd->conference = conference;
2307
2308 aptd->initiator = initiator;
2309 if (initiator) {
2312 /* We don't really care if this fails. If the datastore fails to get set up
2313 * we'll still play the announcement. It's possible that the sound will be
2314 * clipped for the initiator, but that's not the end of the world.
2315 */
2318 }
2319
2320 return aptd;
2321}
2322
2324{
2326 ast_free(aptd);
2327}
2328
2329/*!
2330 * \brief Wait for the initiator of an async playback to be ready
2331 *
2332 * See the description on the async_datastore_info structure for more
2333 * information about what this is about.
2334 *
2335 * \param initiator The channel that queued the async announcement
2336 */
2338{
2339 struct ast_datastore *async_datastore;
2340 struct async_datastore_data *add;
2341
2342 ast_channel_lock(initiator);
2343 async_datastore = ast_channel_datastore_find(initiator, &async_datastore_info, NULL);
2344 ast_channel_unlock(initiator);
2345
2346 if (!async_datastore) {
2347 return;
2348 }
2349
2350 add = async_datastore->data;
2351
2352 ast_mutex_lock(&add->lock);
2353 while (add->wait) {
2354 ast_cond_wait(&add->cond, &add->lock);
2355 }
2356 ast_mutex_unlock(&add->lock);
2357}
2358
2359/*!
2360 * \brief Play an announcement into a confbridge asynchronously
2361 *
2362 * This runs in the playback queue taskprocessor. This ensures that
2363 * all playbacks are handled in sequence and do not play over top one
2364 * another.
2365 *
2366 * \param data An async_playback_task_data
2367 * \return 0
2368 */
2369static int async_playback_task(void *data)
2370{
2371 struct async_playback_task_data *aptd = data;
2372
2373 /* Wait for the initiator to get back in the bridge or be hung up */
2374 if (aptd->initiator) {
2376 }
2377
2378 playback_common(aptd->conference, aptd->filename, aptd->say_number);
2379
2381 return 0;
2382}
2383
2385 const char *filename, int say_number, struct ast_channel *initiator)
2386{
2387 struct async_playback_task_data *aptd;
2388
2389 /* Do not waste resources trying to play files that do not exist */
2391 if (say_number < 0) {
2392 return 0;
2393 }
2394 } else if (!sound_file_exists(filename)) {
2395 return 0;
2396 }
2397
2399 if (!aptd) {
2400 return -1;
2401 }
2402
2404 if (!ast_strlen_zero(filename)) {
2405 ast_log(LOG_WARNING, "Unable to play file '%s' to conference '%s'\n",
2407 } else {
2408 ast_log(LOG_WARNING, "Unable to say number '%d' to conference '%s'\n",
2410 }
2412 return -1;
2413 }
2414
2415 return 0;
2416}
2417
2419 const char *filename, struct ast_channel *initiator)
2420{
2422}
2423
2425{
2426 struct ast_datastore *async_datastore;
2427 struct async_datastore_data *add;
2428
2429 ast_channel_lock(chan);
2430 async_datastore = ast_channel_datastore_find(chan, &async_datastore_info, NULL);
2431 ast_channel_unlock(chan);
2432 if (!async_datastore) {
2433 return;
2434 }
2435
2436 add = async_datastore->data;
2437
2438 ast_mutex_lock(&add->lock);
2439 add->wait = 0;
2440 ast_cond_signal(&add->cond);
2441 ast_mutex_unlock(&add->lock);
2442}
2443
2444/*!
2445 * \brief Play number into the conference bridge
2446 *
2447 * \param conference The conference bridge to say the number into
2448 * \param say_number number to say
2449 *
2450 * \retval 0 success
2451 * \retval -1 failure
2452 */
2453static int play_sound_number(struct confbridge_conference *conference, int say_number)
2454{
2455 return play_sound_helper(conference, NULL, say_number);
2456}
2457
2458static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking)
2459{
2460 struct confbridge_user *user = hook_pvt;
2462 struct ast_json *talking_extras;
2463
2464 conference = ao2_find(conference_bridges, user->conference->name, OBJ_KEY);
2465 if (!conference) {
2466 /* Remove the hook since the conference does not exist. */
2467 return -1;
2468 }
2469
2470 ao2_lock(conference);
2471 user->talking = talking;
2472 ao2_unlock(conference);
2473
2474 talking_extras = ast_json_pack("{s: s, s: b}",
2475 "talking_status", talking ? "on" : "off",
2476 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN));
2477 if (!talking_extras) {
2478 return 0;
2479 }
2480
2481 send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0);
2482 ast_json_unref(talking_extras);
2483 return 0;
2484}
2485
2486static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
2487{
2488 char pin_guess[MAX_PIN+1] = { 0, };
2489 const char *pin = user->u_profile.pin;
2490 char *tmp = pin_guess;
2491 int i, res;
2492 unsigned int len = MAX_PIN;
2493
2494 /*
2495 * NOTE: We have not joined a conference yet so we have to use
2496 * the bridge profile requested by the user.
2497 */
2498
2499 /* give them three tries to get the pin right */
2500 for (i = 0; i < 3; i++) {
2501 if (ast_app_getdata(chan,
2502 conf_get_sound(CONF_SOUND_GET_PIN, user->b_profile.sounds),
2503 tmp, len, 0) >= 0) {
2504 if (!strcasecmp(pin, pin_guess)) {
2505 return 0;
2506 }
2507 }
2508 ast_streamfile(chan,
2509 conf_get_sound(CONF_SOUND_INVALID_PIN, user->b_profile.sounds),
2510 ast_channel_language(chan));
2511 res = ast_waitstream(chan, AST_DIGIT_ANY);
2512 if (res > 0) {
2513 /* Account for digit already read during ivalid pin playback
2514 * resetting pin buf. */
2515 pin_guess[0] = res;
2516 pin_guess[1] = '\0';
2517 tmp = pin_guess + 1;
2518 len = MAX_PIN - 1;
2519 } else {
2520 /* reset pin buf as empty buffer. */
2521 tmp = pin_guess;
2522 len = MAX_PIN;
2523 }
2524 }
2525 return -1;
2526}
2527
2528static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
2529{
2531 pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "TIMEOUT");
2532 return -1;
2533}
2534
2535static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
2536{
2537 char destdir[PATH_MAX];
2538 int res;
2539 int duration = 20;
2540
2541 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
2542
2543 if (ast_mkdir(destdir, 0777) != 0) {
2544 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2545 return -1;
2546 }
2547 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
2548 "%s/confbridge-name-%s-%s", destdir,
2549 conf_name, ast_channel_uniqueid(user->chan));
2550
2552 res = ast_play_and_record(user->chan,
2553 "vm-rec-name",
2554 user->name_rec_location,
2555 10,
2556 "sln",
2557 &duration,
2558 NULL,
2560 0,
2561 NULL);
2562 } else {
2563 res = ast_record_review(user->chan,
2564 "vm-rec-name",
2565 user->name_rec_location,
2566 10,
2567 "sln",
2568 &duration,
2569 NULL);
2570 }
2571
2572 if (res == -1) {
2573 ast_filedelete(user->name_rec_location, NULL);
2574 user->name_rec_location[0] = '\0';
2575 return -1;
2576 }
2577 return 0;
2578}
2579
2582 char filename[0];
2583};
2584
2586 struct confbridge_conference *conference, const char *filename)
2587{
2589
2590 atd = ast_malloc(sizeof(*atd) + strlen(filename) + 1);
2591 if (!atd) {
2592 return NULL;
2593 }
2594
2595 /* Safe */
2596 strcpy(atd->filename, filename);
2597 atd->conference = conference;
2598
2599 return atd;
2600}
2601
2603{
2604 ast_free(atd);
2605}
2606
2607/*!
2608 * \brief Delete user's name file asynchronously
2609 *
2610 * This runs in the playback queue taskprocessor. This ensures that
2611 * sound file is removed after playback is finished and not before.
2612 *
2613 * \param data An async_delete_name_rec_task_data
2614 * \return 0
2615 */
2616static int async_delete_name_rec_task(void *data)
2617{
2618 struct async_delete_name_rec_task_data *atd = data;
2619
2621 ast_log(LOG_DEBUG, "Conference '%s' removed user name file '%s'\n",
2622 atd->conference->name, atd->filename);
2623
2625 return 0;
2626}
2627
2629 const char *filename)
2630{
2632
2634 return 0;
2635 } else if (!sound_file_exists(filename)) {
2636 return 0;
2637 }
2638
2640 if (!atd) {
2641 return -1;
2642 }
2643
2645 ast_log(LOG_WARNING, "Conference '%s' was unable to remove user name file '%s'\n",
2648 return -1;
2649 }
2650
2651 return 0;
2652}
2653
2654static int join_callback(struct ast_bridge_channel *bridge_channel, void *ignore)
2655{
2656 async_play_sound_ready(bridge_channel->chan);
2657 return 0;
2658}
2659
2664};
2665
2666static int send_event_hook_callback(struct ast_bridge_channel *bridge_channel, void *data)
2667{
2668 struct confbridge_hook_data *hook_data = data;
2669
2670 if (hook_data->hook_type == AST_BRIDGE_HOOK_TYPE_JOIN) {
2671 send_join_event(hook_data->user, hook_data->conference);
2672 } else {
2673 send_leave_event(hook_data->user, hook_data->conference);
2674 }
2675
2676 return 0;
2677}
2678
2679/*! \brief The ConfBridge application */
2680static int confbridge_exec(struct ast_channel *chan, const char *data)
2681{
2682 int res = 0, volume_adjustments[2];
2683 int quiet = 0;
2684 int async_delete_task_pushed = 0;
2685 char *parse;
2686 const char *b_profile_name = NULL;
2687 const char *u_profile_name = NULL;
2688 const char *menu_profile_name = NULL;
2689 struct confbridge_conference *conference = NULL;
2690 struct confbridge_user user = {
2691 .chan = chan,
2692 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
2693 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
2694 .tech_args.drop_silence = 0,
2695 };
2696 struct confbridge_hook_data *join_hook_data;
2697 struct confbridge_hook_data *leave_hook_data;
2698
2700 AST_APP_ARG(conf_name);
2701 AST_APP_ARG(b_profile_name);
2702 AST_APP_ARG(u_profile_name);
2703 AST_APP_ARG(menu_profile_name);
2704 );
2705
2706 if (ast_bridge_features_init(&user.features)) {
2707 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2708 res = -1;
2709 goto confbridge_cleanup;
2710 }
2711
2712 /* We need to make a copy of the input string if we are going to modify it! */
2713 parse = ast_strdupa(data);
2714
2716
2717 if (ast_strlen_zero(args.conf_name)) {
2718 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2719 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
2720 res = -1;
2721 goto confbridge_cleanup;
2722 }
2723
2724 if (strlen(args.conf_name) >= MAX_CONF_NAME) {
2725 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2726 ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
2727 res = -1;
2728 goto confbridge_cleanup;
2729 }
2730
2731 /* bridge profile name */
2732 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
2733 b_profile_name = args.b_profile_name;
2734 }
2735 if (!conf_find_bridge_profile(chan, b_profile_name, &user.b_profile)) {
2736 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2737 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name ?
2738 b_profile_name : DEFAULT_BRIDGE_PROFILE);
2739 res = -1;
2740 goto confbridge_cleanup;
2741 }
2742
2743 /* user profile name */
2744 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
2745 u_profile_name = args.u_profile_name;
2746 }
2747 if (!conf_find_user_profile(chan, u_profile_name, &user.u_profile)) {
2748 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2749 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name ?
2750 u_profile_name : DEFAULT_USER_PROFILE);
2751 res = -1;
2752 goto confbridge_cleanup;
2753 }
2754
2755 /* If channel hasn't been answered already, answer it, unless we're explicitly not supposed to */
2756 if ((ast_channel_state(chan) != AST_STATE_UP) && (ast_test_flag(&user.u_profile, USER_OPT_ANSWER_CHANNEL))) {
2757 ast_answer(chan);
2758 }
2759
2760 quiet = ast_test_flag(&user.u_profile, USER_OPT_QUIET);
2761
2762 /* ask for a PIN immediately after finding user profile. This has to be
2763 * prompted for requardless of quiet setting. */
2764 if (!ast_strlen_zero(user.u_profile.pin)) {
2765 if (conf_get_pin(chan, &user)) {
2766 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2767 res = -1; /* invalid PIN */
2768 goto confbridge_cleanup;
2769 }
2770 }
2771
2772 /* See if we need them to record a intro name */
2773 if (!quiet &&
2776 if (conf_rec_name(&user, args.conf_name)) {
2777 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2778 res = -1; /* Hangup during name recording */
2779 goto confbridge_cleanup;
2780 }
2781 }
2782
2783 /* menu name */
2784 if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) {
2785 menu_profile_name = args.menu_profile_name;
2786 }
2787
2788 if (conf_set_menu_to_user(chan, &user, menu_profile_name)) {
2789 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2790 ast_log(LOG_WARNING, "Conference menu profile %s does not exist\n", menu_profile_name ?
2791 menu_profile_name : DEFAULT_MENU_PROFILE);
2792 res = -1;
2793 goto confbridge_cleanup;
2794 }
2795
2796 /* Set if DTMF should pass through for this user or not */
2797 if (ast_test_flag(&user.u_profile, USER_OPT_DTMF_PASS)) {
2798 user.features.dtmf_passthrough = 1;
2799 } else {
2800 user.features.dtmf_passthrough = 0;
2801 }
2802
2803 /* Set if text messaging is enabled for this user or not */
2804 if (ast_test_flag(&user.u_profile, USER_OPT_TEXT_MESSAGING)) {
2805 user.features.text_messaging = 1;
2806 } else {
2807 user.features.text_messaging = 0;
2808 }
2809
2810 /* Set dsp threshold values if present */
2811 if (user.u_profile.talking_threshold) {
2812 user.tech_args.talking_threshold = user.u_profile.talking_threshold;
2813 }
2814 if (user.u_profile.silence_threshold) {
2815 user.tech_args.silence_threshold = user.u_profile.silence_threshold;
2816 }
2817
2818 /* Set a talker indicate call back if talking detection is requested */
2819 if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) {
2822 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2823 res = -1;
2824 goto confbridge_cleanup;
2825 }
2826 }
2827
2828 /* Look for a conference bridge matching the provided name */
2829 if (!(conference = join_conference_bridge(args.conf_name, &user))) {
2830 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2831 res = -1;
2832 goto confbridge_cleanup;
2833 }
2834
2835 /* Keep a copy of volume adjustments so we can restore them later if need be */
2836 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
2837 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
2838
2839 if (ast_test_flag(&user.u_profile, USER_OPT_DROP_SILENCE)) {
2840 user.tech_args.drop_silence = 1;
2841 }
2842
2843 if (ast_test_flag(&user.u_profile, USER_OPT_JITTERBUFFER)) {
2844 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
2845 }
2846
2847 if (ast_test_flag(&user.u_profile, USER_OPT_DENOISE)) {
2848 ast_func_write(chan, "DENOISE(rx)", "on");
2849 }
2850
2851 /* if this user has a intro, play it before entering */
2852 if (!ast_strlen_zero(user.name_rec_location)) {
2854 play_sound_file(conference, user.name_rec_location);
2858 }
2859
2860 if (!quiet) {
2861 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference->b_profile.sounds);
2862
2863 /* if hear_own_join_sound is enabled play the Join sound to everyone */
2865 if (strcmp(conference->b_profile.language, ast_channel_language(chan))) {
2866 ast_stream_and_wait(chan, join_sound, "");
2868 play_sound_file(conference, join_sound);
2870 } else {
2871 async_play_sound_file(conference, join_sound, chan);
2872 }
2873 /* if hear_own_join_sound is disabled only play the Join sound to just the conference */
2874 } else {
2876 play_sound_file(conference, join_sound);
2878 }
2879 }
2880
2881 if (user.u_profile.timeout) {
2883 0,
2884 user.u_profile.timeout * 1000,
2886 NULL,
2887 NULL,
2889 }
2890
2891 /* See if we need to automatically set this user as a video source or not */
2893
2895
2896 join_hook_data = ast_malloc(sizeof(*join_hook_data));
2897 if (!join_hook_data) {
2898 res = -1;
2899 goto confbridge_cleanup;
2900 }
2901 join_hook_data->user = &user;
2902 join_hook_data->conference = conference;
2903 join_hook_data->hook_type = AST_BRIDGE_HOOK_TYPE_JOIN;
2905 join_hook_data, ast_free_ptr, 0);
2906 if (res) {
2907 ast_free(join_hook_data);
2908 ast_log(LOG_ERROR, "Couldn't add bridge join hook for channel '%s'\n", ast_channel_name(chan));
2909 goto confbridge_cleanup;
2910 }
2911
2912 leave_hook_data = ast_malloc(sizeof(*leave_hook_data));
2913 if (!leave_hook_data) {
2914 /* join_hook_data is cleaned up by ast_bridge_features_cleanup via the goto */
2915 res = -1;
2916 goto confbridge_cleanup;
2917 }
2918 leave_hook_data->user = &user;
2919 leave_hook_data->conference = conference;
2920 leave_hook_data->hook_type = AST_BRIDGE_HOOK_TYPE_LEAVE;
2922 leave_hook_data, ast_free_ptr, 0);
2923 if (res) {
2924 /* join_hook_data is cleaned up by ast_bridge_features_cleanup via the goto */
2925 ast_free(leave_hook_data);
2926 ast_log(LOG_ERROR, "Couldn't add bridge leave hook for channel '%s'\n", ast_channel_name(chan));
2927 goto confbridge_cleanup;
2928 }
2929
2930 if (ast_bridge_join_hook(&user.features, join_callback, NULL, NULL, 0)) {
2932 }
2933
2935 chan,
2936 NULL,
2937 &user.features,
2938 &user.tech_args,
2939 0);
2940
2941 /* This is a catch-all in case joining the bridge failed or for some reason
2942 * an async announcement got queued up and hasn't been told to play yet
2943 */
2945
2946 if (!user.kicked && ast_check_hangup(chan)) {
2947 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "HANGUP");
2948 }
2949
2950 /* if we're shutting down, don't attempt to do further processing */
2951 if (ast_shutting_down()) {
2952 /*
2953 * Not taking any new calls at this time. We cannot create
2954 * the announcer channel if this is the first channel into
2955 * the conference and we certainly cannot create any
2956 * recording channel.
2957 */
2959 conference = NULL;
2960 goto confbridge_cleanup;
2961 }
2962
2963 /* If this user was a video source, we need to clean up and possibly pick a new source. */
2965
2966 /* if this user has a intro, play it when leaving */
2967 if (!quiet && !ast_strlen_zero(user.name_rec_location)) {
2968 async_play_sound_file(conference, user.name_rec_location, NULL);
2971 async_delete_name_rec(conference, user.name_rec_location);
2972 async_delete_task_pushed = 1;
2973 }
2974
2975 /* play the leave sound */
2976 if (!quiet) {
2977 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference->b_profile.sounds);
2978 async_play_sound_file(conference, leave_sound, NULL);
2979 }
2980
2981 /* If the user was kicked from the conference play back the audio prompt for it */
2982 if (!quiet && user.kicked) {
2983 res = ast_stream_and_wait(chan,
2985 "");
2986 }
2987
2988 /* Easy as pie, depart this channel from the conference bridge */
2990 conference = NULL;
2991
2992 /* Restore volume adjustments to previous values in case they were changed */
2993 if (volume_adjustments[0]) {
2994 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
2995 }
2996 if (volume_adjustments[1]) {
2997 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
2998 }
2999
3000confbridge_cleanup:
3001 if (!async_delete_task_pushed && !ast_strlen_zero(user.name_rec_location)) {
3002 ast_filedelete(user.name_rec_location, NULL);
3003 }
3006 return res;
3007}
3008
3010 struct confbridge_user *user,
3011 struct ast_bridge_channel *bridge_channel)
3012{
3013 int mute;
3014
3015 /* Toggle user level mute request. */
3016 mute = !user->muted;
3018
3019 return play_file(bridge_channel, NULL,
3022}
3023
3025 struct confbridge_user *user,
3026 struct ast_bridge_channel *bridge_channel)
3027{
3028 unsigned int binaural;
3029 ast_bridge_channel_lock_bridge(bridge_channel);
3030 binaural = !bridge_channel->binaural_suspended;
3031 bridge_channel->binaural_suspended = binaural;
3032 ast_bridge_unlock(bridge_channel->bridge);
3033 return play_file(bridge_channel, NULL, (binaural ?
3034 conf_get_sound(CONF_SOUND_BINAURAL_OFF, user->b_profile.sounds) :
3035 conf_get_sound(CONF_SOUND_BINAURAL_ON, user->b_profile.sounds))) < 0;
3036}
3037
3039{
3040 struct confbridge_user *cur_user = NULL;
3041 const char *sound_to_play;
3042 int mute;
3043
3045
3046 /* Toggle bridge level mute request. */
3047 mute = !conference->muted;
3048 conference->muted = mute;
3049
3051 if (!ast_test_flag(&cur_user->u_profile, USER_OPT_ADMIN)) {
3052 /* Set user level to bridge level mute request. */
3053 cur_user->muted = mute;
3054 conf_update_user_mute(cur_user);
3055 }
3056 }
3057
3059
3060 sound_to_play = conf_get_sound(
3063
3064 if (strcmp(conference->b_profile.language, ast_channel_language(user->chan))) {
3065 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
3066 ast_stream_and_wait(user->chan, sound_to_play, "");
3067
3068 /* Announce to the group that all participants are muted */
3070 play_sound_file(conference, sound_to_play);
3072 } else {
3073 /* Playing the sound asynchronously lets the sound be heard by everyone at once */
3074 async_play_sound_file(conference, sound_to_play, user->chan);
3075 }
3076
3077 return 0;
3078}
3079
3080static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
3081{
3082 char *file_copy = ast_strdupa(playback_file);
3083 char *file = NULL;
3084
3085 while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
3086 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
3087 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
3088 return -1;
3089 }
3090 }
3091 return 0;
3092}
3093
3095 struct confbridge_user *user,
3096 struct ast_bridge_channel *bridge_channel,
3097 struct conf_menu *menu,
3098 const char *playback_file,
3099 const char *cur_dtmf,
3100 int *stop_prompts)
3101{
3102 int i;
3103 int digit = 0;
3104 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
3105 struct conf_menu_entry new_menu_entry = { { 0, }, };
3106 char *file_copy = ast_strdupa(playback_file);
3107 char *file = NULL;
3108
3109 while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
3110 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
3111 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
3112 return -1;
3113 }
3114
3115 /* now wait for more digits. */
3116 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
3117 /* streaming finished and no DTMF was entered */
3118 continue;
3119 } else if (digit == -1) {
3120 /* error */
3121 return -1;
3122 } else {
3123 break; /* dtmf was entered */
3124 }
3125 }
3126 if (!digit) {
3127 /* streaming finished on all files and no DTMF was entered */
3128 return -1;
3129 }
3130 ast_stopstream(bridge_channel->chan);
3131
3132 /* If we get here, then DTMF has been entered, This means no
3133 * additional prompts should be played for this menu entry */
3134 *stop_prompts = 1;
3135
3136 /* If a digit was pressed during the payback, update
3137 * the dtmf string and look for a new menu entry in the
3138 * menu structure */
3139 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
3140 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
3141 dtmf[i] = cur_dtmf[i];
3142 if (!dtmf[i]) {
3143 dtmf[i] = (char) digit;
3144 dtmf[i + 1] = '\0';
3145 i = -1;
3146 break;
3147 }
3148 }
3149 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
3150 * If this is the case, no new DTMF sequence should be looked for. */
3151 if (i != -1) {
3152 return 0;
3153 }
3154
3155 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
3156 execute_menu_entry(conference,
3157 user,
3158 bridge_channel,
3159 &new_menu_entry, menu);
3160 conf_menu_entry_destroy(&new_menu_entry);
3161 }
3162 return 0;
3163}
3164
3165static int action_kick_last(struct confbridge_conference *conference,
3166 struct ast_bridge_channel *bridge_channel,
3167 struct confbridge_user *user)
3168{
3169 struct confbridge_user *last_user = NULL;
3170 int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
3171
3172 if (!isadmin) {
3173 play_file(bridge_channel, NULL,
3175 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
3176 ast_channel_name(bridge_channel->chan),
3177 conference->name);
3178 return -1;
3179 }
3180
3182 last_user = AST_LIST_LAST(&conference->active_list);
3183 if (!last_user) {
3185 return 0;
3186 }
3187
3188 if (last_user == user || ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN)) {
3190 play_file(bridge_channel, NULL,
3192 } else if (!last_user->kicked) {
3193 last_user->kicked = 1;
3194 pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
3197 }
3198
3199 return 0;
3200}
3201
3202static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
3203{
3204 struct ast_pbx_args args;
3205 struct ast_pbx *pbx;
3206 char *exten;
3207 char *context;
3208 int priority;
3209 int res;
3210
3211 memset(&args, 0, sizeof(args));
3212 args.no_hangup_chan = 1;
3213
3214 ast_channel_lock(bridge_channel->chan);
3215
3216 /*save off*/
3217 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
3218 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
3219 priority = ast_channel_priority(bridge_channel->chan);
3220 pbx = ast_channel_pbx(bridge_channel->chan);
3221 ast_channel_pbx_set(bridge_channel->chan, NULL);
3222
3223 /*set new*/
3224 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
3225 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
3226 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
3227
3228 ast_channel_unlock(bridge_channel->chan);
3229
3230 /*execute*/
3231 res = ast_pbx_run_args(bridge_channel->chan, &args);
3232
3233 /*restore*/
3234 ast_channel_lock(bridge_channel->chan);
3235
3236 ast_channel_exten_set(bridge_channel->chan, exten);
3237 ast_channel_context_set(bridge_channel->chan, context);
3238 ast_channel_priority_set(bridge_channel->chan, priority);
3239 ast_channel_pbx_set(bridge_channel->chan, pbx);
3240
3241 ast_channel_unlock(bridge_channel->chan);
3242
3243 return res;
3244}
3245
3246static int execute_menu_entry(struct confbridge_conference *conference,
3247 struct confbridge_user *user,
3248 struct ast_bridge_channel *bridge_channel,
3249 struct conf_menu_entry *menu_entry,
3250 struct conf_menu *menu)
3251{
3252 struct conf_menu_action *menu_action;
3253 int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
3254 int stop_prompts = 0;
3255 int res = 0;
3256
3257 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
3258 switch (menu_action->id) {
3260 res |= action_toggle_mute(conference, user, bridge_channel);
3261 break;
3263 action_toggle_binaural(conference, user, bridge_channel);
3264 break;
3266 if (!isadmin) {
3267 break;
3268 }
3270 break;
3272 announce_user_count(conference, user, bridge_channel);
3273 break;
3275 if (!stop_prompts) {
3276 res |= action_playback(bridge_channel, menu_action->data.playback_file);
3277 ast_test_suite_event_notify("CONF_MENU_PLAYBACK",
3278 "Message: %s\r\nChannel: %s",
3279 menu_action->data.playback_file, ast_channel_name(bridge_channel->chan));
3280 }
3281 break;
3284 break;
3287 break;
3291 break;
3295 break;
3299 break;
3303 break;
3305 if (!(stop_prompts)) {
3306 res |= action_playback_and_continue(conference,
3307 user,
3308 bridge_channel,
3309 menu,
3310 menu_action->data.playback_file,
3311 menu_entry->dtmf,
3312 &stop_prompts);
3313 }
3314 break;
3316 res |= action_dialplan_exec(bridge_channel, menu_action);
3317 break;
3319 if (!isadmin) {
3320 break;
3321 }
3322 conference->locked = (!conference->locked ? 1 : 0);
3323 res |= play_file(bridge_channel, NULL,
3326 conference->b_profile.sounds)) < 0;
3327 break;
3329 res |= action_kick_last(conference, bridge_channel, user);
3330 break;
3331 case MENU_ACTION_LEAVE:
3332 pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF");
3333 ao2_lock(conference);
3334 ast_bridge_remove(conference->bridge, bridge_channel->chan);
3335 ast_test_suite_event_notify("CONF_MENU_LEAVE",
3336 "Channel: %s",
3337 ast_channel_name(bridge_channel->chan));
3338 ao2_unlock(conference);
3339 break;
3340 case MENU_ACTION_NOOP:
3341 break;
3343 ao2_lock(conference);
3344 if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
3345 ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan);
3346 }
3347 ao2_unlock(conference);
3348 break;
3350 handle_video_on_exit(conference, bridge_channel->chan);
3351 break;
3352 }
3353 }
3354 return res;
3355}
3356
3357int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
3358 struct confbridge_user *user,
3359 struct conf_menu_entry *menu_entry,
3360 struct conf_menu *menu)
3361{
3362 /* See if music on hold is playing */
3364
3365 /* execute the list of actions associated with this menu entry */
3366 execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu);
3367
3368 /* See if music on hold needs to be started back up again */
3370
3371 async_play_sound_ready(bridge_channel->chan);
3372
3373 return 0;
3374}
3375
3377 const char *channel)
3378{
3379 int res = -1;
3380 int match;
3381 struct confbridge_user *user = NULL;
3382 int all = !strcasecmp("all", channel);
3383 int participants = !strcasecmp("participants", channel);
3384
3385 SCOPED_AO2LOCK(bridge_lock, conference);
3386
3388 if (user->kicked) {
3389 continue;
3390 }
3391 match = !strcasecmp(channel, ast_channel_name(user->chan));
3392 if (match || all
3393 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3394 user->kicked = 1;
3395 pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
3397 res = 0;
3398 if (match) {
3399 return res;
3400 }
3401 }
3402 }
3404 if (user->kicked) {
3405 continue;
3406 }
3407 match = !strcasecmp(channel, ast_channel_name(user->chan));
3408 if (match || all
3409 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3410 user->kicked = 1;
3411 pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
3413 res = 0;
3414 if (match) {
3415 return res;
3416 }
3417 }
3418 }
3419
3420 return res;
3421}
3422
3423static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
3424{
3425 int which = 0;
3426 struct confbridge_conference *conference;
3427 char *res = NULL;
3428 int wordlen = strlen(word);
3429 struct ao2_iterator iter;
3430
3432 while ((conference = ao2_iterator_next(&iter))) {
3433 if (!strncasecmp(conference->name, word, wordlen) && ++which > state) {
3434 res = ast_strdup(conference->name);
3435 ao2_ref(conference, -1);
3436 break;
3437 }
3438 ao2_ref(conference, -1);
3439 }
3440 ao2_iterator_destroy(&iter);
3441
3442 return res;
3443}
3444
3445static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
3446{
3447 int which = 0;
3448 RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
3449 struct confbridge_user *user;
3450 char *res = NULL;
3451 int wordlen = strlen(word);
3452
3453 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3454 if (!conference) {
3455 return NULL;
3456 }
3457
3458 if (!strncasecmp("all", word, wordlen) && ++which > state) {
3459 return ast_strdup("all");
3460 }
3461
3462 if (!strncasecmp("participants", word, wordlen) && ++which > state) {
3463 return ast_strdup("participants");
3464 }
3465
3466 {
3467 SCOPED_AO2LOCK(bridge_lock, conference);
3469 if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
3470 res = ast_strdup(ast_channel_name(user->chan));
3471 return res;
3472 }
3473 }
3475 if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
3476 res = ast_strdup(ast_channel_name(user->chan));
3477 return res;
3478 }
3479 }
3480 }
3481
3482 return NULL;
3483}
3484
3485static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3486{
3487 struct confbridge_conference *conference;
3488 int not_found;
3489
3490 switch (cmd) {
3491 case CLI_INIT:
3492 e->command = "confbridge kick";
3493 e->usage =
3494 "Usage: confbridge kick <conference> <channel>\n"
3495 " Kicks a channel out of the conference bridge.\n"
3496 " (all to kick everyone, participants to kick non-admins).\n";
3497 return NULL;
3498 case CLI_GENERATE:
3499 if (a->pos == 2) {
3500 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3501 }
3502 if (a->pos == 3) {
3503 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3504 }
3505 return NULL;
3506 }
3507
3508 if (a->argc != 4) {
3509 return CLI_SHOWUSAGE;
3510 }
3511
3512 conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
3513 if (!conference) {
3514 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3515 return CLI_SUCCESS;
3516 }
3517 not_found = kick_conference_participant(conference, a->argv[3]);
3518 ao2_ref(conference, -1);
3519 if (not_found) {
3520 if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
3521 ast_cli(a->fd, "No participants found!\n");
3522 } else {
3523 ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
3524 }
3525 return CLI_SUCCESS;
3526 }
3527 ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]);
3528 return CLI_SUCCESS;
3529}
3530
3531static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
3532{
3533 char flag_str[6 + 1];/* Max flags + terminator */
3534 int pos = 0;
3535
3536 /* Build flags column string. */
3537 if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
3538 flag_str[pos++] = 'A';
3539 }
3540 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
3541 flag_str[pos++] = 'M';
3542 }
3543 if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
3544 flag_str[pos++] = 'W';
3545 }
3546 if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) {
3547 flag_str[pos++] = 'E';
3548 }
3549 if (user->muted) {
3550 flag_str[pos++] = 'm';
3551 }
3552 if (waiting) {
3553 flag_str[pos++] = 'w';
3554 }
3555 flag_str[pos] = '\0';
3556
3557 ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n",
3558 ast_channel_name(user->chan),
3559 flag_str,
3560 user->u_profile.name,
3561 user->conference->b_profile.name,
3562 user->menu_name,
3564 ast_channel_caller(user->chan)->id.number.str, "<unknown>"));
3565}
3566
3567static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3568{
3569 struct confbridge_conference *conference;
3570
3571 switch (cmd) {
3572 case CLI_INIT:
3573 e->command = "confbridge list";
3574 e->usage =
3575 "Usage: confbridge list [<name>]\n"
3576 " Lists all currently active conference bridges or a specific conference bridge.\n"
3577 "\n"
3578 " When a conference bridge name is provided, flags may be shown for users. Below\n"
3579 " are the flags and what they represent.\n"
3580 "\n"
3581 " Flags:\n"
3582 " A - The user is an admin\n"
3583 " M - The user is a marked user\n"
3584 " W - The user must wait for a marked user to join\n"
3585 " E - The user will be kicked after the last marked user leaves the conference\n"
3586 " m - The user is muted\n"
3587 " w - The user is waiting for a marked user to join\n";
3588 return NULL;
3589 case CLI_GENERATE:
3590 if (a->pos == 2) {
3591 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3592 }
3593 return NULL;
3594 }
3595
3596 if (a->argc == 2) {
3597 struct ao2_iterator iter;
3598
3599 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked Muted\n");
3600 ast_cli(a->fd, "================================ ====== ====== ====== =====\n");
3602 while ((conference = ao2_iterator_next(&iter))) {
3603 ast_cli(a->fd, "%-32s %6u %6u %-6s %s\n",
3604 conference->name,
3605 conference->activeusers + conference->waitingusers,
3606 conference->markedusers,
3607 AST_CLI_YESNO(conference->locked),
3608 AST_CLI_YESNO(conference->muted));
3609 ao2_ref(conference, -1);
3610 }
3611 ao2_iterator_destroy(&iter);
3612 return CLI_SUCCESS;
3613 }
3614
3615 if (a->argc == 3) {
3616 struct confbridge_user *user;
3617
3619 if (!conference) {
3620 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3621 return CLI_SUCCESS;
3622 }
3623 ast_cli(a->fd, "Channel Flags User Profile Bridge Profile Menu CallerID\n");
3624 ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n");
3628 }
3631 }
3633 ao2_ref(conference, -1);
3634 return CLI_SUCCESS;
3635 }
3636
3637 return CLI_SHOWUSAGE;
3638}
3639
3640/*! \internal
3641 * \brief finds a conference by name and locks/unlocks.
3642 *
3643 * \retval 0 success
3644 * \retval -1 conference not found
3645 */
3646static int generic_lock_unlock_helper(int lock, const char *conference_name)
3647{
3648 struct confbridge_conference *conference;
3649 int res = 0;
3650
3651 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3652 if (!conference) {
3653 return -1;
3654 }
3655 ao2_lock(conference);
3656 conference->locked = lock;
3657 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name);
3658 ao2_unlock(conference);
3659 ao2_ref(conference, -1);
3660
3661 return res;
3662}
3663
3664/*! \internal
3665 * \brief finds a conference user by channel name and mutes/unmutes them.
3666 *
3667 * \retval 0 success
3668 * \retval -1 conference not found
3669 * \retval -2 user not found
3670 */
3671static int generic_mute_unmute_helper(int mute, const char *conference_name,
3672 const char *chan_name)
3673{
3674 RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
3675 struct confbridge_user *user;
3676 int all = !strcasecmp("all", chan_name);
3677 int participants = !strcasecmp("participants", chan_name);
3678 int res = -2;
3679
3680 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3681 if (!conference) {
3682 return -1;
3683 }
3684
3685 {
3686 SCOPED_AO2LOCK(bridge_lock, conference);
3688 int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
3689 strlen(chan_name));
3690 if (match || all
3691 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3693 res = 0;
3694 if (match) {
3695 return res;
3696 }
3697 }
3698 }
3699
3701 int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
3702 strlen(chan_name));
3703 if (match || all
3704 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3706 res = 0;
3707 if (match) {
3708 return res;
3709 }
3710 }
3711 }
3712 }
3713
3714 return res;
3715}
3716
3717static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
3718{
3719 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
3720
3721 if (res == -1) {
3722 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3723 return -1;
3724 } else if (res == -2) {
3725 if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
3726 ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]);
3727 } else {
3728 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
3729 }
3730 return -1;
3731 }
3732 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
3733 return 0;
3734}
3735
3736static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3737{
3738 switch (cmd) {
3739 case CLI_INIT:
3740 e->command = "confbridge mute";
3741 e->usage =
3742 "Usage: confbridge mute <conference> <channel>\n"
3743 " Mute a channel in a conference.\n"
3744 " (all to mute everyone, participants to mute non-admins)\n"
3745 " If the specified channel is a prefix,\n"
3746 " the action will be taken on the first\n"
3747 " matching channel.\n";
3748 return NULL;
3749 case CLI_GENERATE:
3750 if (a->pos == 2) {
3751 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3752 }
3753 if (a->pos == 3) {
3754 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3755 }
3756 return NULL;
3757 }
3758 if (a->argc != 4) {
3759 return CLI_SHOWUSAGE;
3760 }
3761
3763
3764 return CLI_SUCCESS;
3765}
3766
3767static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3768{
3769 switch (cmd) {
3770 case CLI_INIT:
3771 e->command = "confbridge unmute";
3772 e->usage =
3773 "Usage: confbridge unmute <conference> <channel>\n"
3774 " Unmute a channel in a conference.\n"
3775 " (all to unmute everyone, participants to unmute non-admins)\n"
3776 " If the specified channel is a prefix,\n"
3777 " the action will be taken on the first\n"
3778 " matching channel.\n";
3779 return NULL;
3780 case CLI_GENERATE:
3781 if (a->pos == 2) {
3782 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3783 }
3784 if (a->pos == 3) {
3785 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3786 }
3787 return NULL;
3788 }
3789 if (a->argc != 4) {
3790 return CLI_SHOWUSAGE;
3791 }
3792
3794
3795 return CLI_SUCCESS;
3796}
3797
3798static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3799{
3800 switch (cmd) {
3801 case CLI_INIT:
3802 e->command = "confbridge lock";
3803 e->usage =
3804 "Usage: confbridge lock <conference>\n"
3805 " Lock a conference. While locked, no new non-admins\n"
3806 " may join the conference.\n";
3807 return NULL;
3808 case CLI_GENERATE:
3809 if (a->pos == 2) {
3810 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3811 }
3812 return NULL;
3813 }
3814 if (a->argc != 3) {
3815 return CLI_SHOWUSAGE;
3816 }
3817 if (generic_lock_unlock_helper(1, a->argv[2])) {
3818 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
3819 } else {
3820 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
3821 }
3822 return CLI_SUCCESS;
3823}
3824
3825static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3826{
3827 switch (cmd) {
3828 case CLI_INIT:
3829 e->command = "confbridge unlock";
3830 e->usage =
3831 "Usage: confbridge unlock <conference>\n"
3832 " Unlock a previously locked conference.\n";
3833 return NULL;
3834 case CLI_GENERATE:
3835 if (a->pos == 2) {
3836 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3837 }
3838 return NULL;
3839 }
3840 if (a->argc != 3) {
3841 return CLI_SHOWUSAGE;
3842 }
3843 if (generic_lock_unlock_helper(0, a->argv[2])) {
3844 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
3845 } else {
3846 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
3847 }
3848 return CLI_SUCCESS;
3849}
3850
3851static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3852{
3853 const char *rec_file = NULL;
3854 struct confbridge_conference *conference;
3855
3856 switch (cmd) {
3857 case CLI_INIT:
3858 e->command = "confbridge record start";
3859 e->usage =
3860 "Usage: confbridge record start <conference> <file>\n"
3861 " <file> is optional, Otherwise the bridge profile\n"
3862 " record file will be used. If the bridge profile\n"
3863 " has no record file specified, a file will automatically\n"
3864 " be generated in the monitor directory\n";
3865 return NULL;
3866 case CLI_GENERATE:
3867 if (a->pos == 3) {
3868 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3869 }
3870 return NULL;
3871 }
3872 if (a->argc < 4) {
3873 return CLI_SHOWUSAGE;
3874 }
3875 if (a->argc == 5) {
3876 rec_file = a->argv[4];
3877 }
3878
3879 conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
3880 if (!conference) {
3881 ast_cli(a->fd, "Conference not found.\n");
3882 return CLI_FAILURE;
3883 }
3884 ao2_lock(conference);
3885 if (conf_is_recording(conference)) {
3886 ast_cli(a->fd, "Conference is already being recorded.\n");
3887 ao2_unlock(conference);
3888 ao2_ref(conference, -1);
3889 return CLI_SUCCESS;
3890 }
3891 if (!ast_strlen_zero(rec_file)) {
3892 ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
3893 }
3894
3895 if (conf_start_record(conference)) {
3896 ast_cli(a->fd, "Could not start recording due to internal error.\n");
3897 ao2_unlock(conference);
3898 ao2_ref(conference, -1);
3899 return CLI_FAILURE;
3900 }
3901 ao2_unlock(conference);
3902
3903 ast_cli(a->fd, "Recording started\n");
3904 ao2_ref(conference, -1);
3905 return CLI_SUCCESS;
3906}
3907
3908static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3909{
3910 struct confbridge_conference *conference;
3911 int ret;
3912
3913 switch (cmd) {
3914 case CLI_INIT:
3915 e->command = "confbridge record stop";
3916 e->usage =
3917 "Usage: confbridge record stop <conference>\n"
3918 " Stop a previously started recording.\n";
3919 return NULL;
3920 case CLI_GENERATE:
3921 if (a->pos == 3) {
3922 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3923 }
3924 return NULL;
3925 }
3926 if (a->argc != 4) {
3927 return CLI_SHOWUSAGE;
3928 }
3929
3930 conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
3931 if (!conference) {
3932 ast_cli(a->fd, "Conference not found.\n");
3933 return CLI_SUCCESS;
3934 }
3935 ao2_lock(conference);
3936 ret = conf_stop_record(conference);
3937 ao2_unlock(conference);
3938 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
3939 ao2_ref(conference, -1);
3940 return CLI_SUCCESS;
3941}
3942
3944 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
3945 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
3946 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."),
3947 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."),
3948 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
3949 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
3950 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
3951 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
3952};
3954 .name = "CONFBRIDGE",
3955 .write = func_confbridge_helper,
3956};
3957
3958static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
3960 .name = "CONFBRIDGE_INFO",
3961 .read = func_confbridge_info,
3962};
3963
3964static int func_confbridge_channels(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
3965{
3966 char *parse, *outbuf;
3967 struct confbridge_conference *conference;
3968 struct confbridge_user *user;
3969 int bytes, count = 0;
3970 size_t outlen;
3973 AST_APP_ARG(confno);
3974 );
3975
3976 /* parse all the required arguments and make sure they exist. */
3977 if (ast_strlen_zero(data)) {
3978 return -1;
3979 }
3980 parse = ast_strdupa(data);
3982 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
3983 ast_log(LOG_WARNING, "Usage: %s(category,confno)", cmd);
3984 return -1;
3985 }
3987 if (!conference) {
3988 ast_debug(1, "No such conference: %s\n", args.confno);
3989 return -1;
3990 }
3991
3992 outbuf = buf;
3993 outlen = len;
3994
3996 if (!strcasecmp(args.type, "parties")) {
3998 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
3999 outbuf += bytes;
4000 outlen -= bytes;
4001 }
4003 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4004 outbuf += bytes;
4005 outlen -= bytes;
4006 }
4007 } else if (!strcasecmp(args.type, "active")) {
4009 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4010 outbuf += bytes;
4011 outlen -= bytes;
4012 }
4013 } else if (!strcasecmp(args.type, "waiting")) {
4015 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4016 outbuf += bytes;
4017 outlen -= bytes;
4018 }
4019 } else if (!strcasecmp(args.type, "admins")) {
4021 if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
4022 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4023 outbuf += bytes;
4024 outlen -= bytes;
4025 }
4026 }
4027 } else if (!strcasecmp(args.type, "marked")) {
4029 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
4030 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4031 outbuf += bytes;
4032 outlen -= bytes;
4033 }
4034 }
4035 } else {
4036 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to %s.\n", args.type, cmd);
4037 }
4039 ao2_ref(conference, -1);
4040 return 0;
4041}
4042
4044 .name = "CONFBRIDGE_CHANNELS",
4046};
4047
4048static int action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
4049{
4050 struct ast_channel_snapshot *snapshot;
4051 struct ast_str *snap_str;
4052
4054 if (!snapshot) {
4055 return 0;
4056 }
4057
4058 snap_str = ast_manager_build_channel_state_string(snapshot);
4059 if (!snap_str) {
4060 ao2_ref(snapshot, -1);
4061 return 0;
4062 }
4063
4064 astman_append(s,
4065 "Event: ConfbridgeList\r\n"
4066 "%s"
4067 "Conference: %s\r\n"
4068 "Admin: %s\r\n"
4069 "MarkedUser: %s\r\n"
4070 "WaitMarked: %s\r\n"
4071 "EndMarked: %s\r\n"
4072 "EndMarkedAny: %s\r\n"
4073 "Waiting: %s\r\n"
4074 "Muted: %s\r\n"
4075 "Talking: %s\r\n"
4076 "AnsweredTime: %d\r\n"
4077 "%s"
4078 "\r\n",
4079 id_text,
4080 conference->name,
4086 AST_YESNO(waiting),
4087 AST_YESNO(user->muted),
4088 AST_YESNO(user->talking),
4090 ast_str_buffer(snap_str));
4091
4092 ast_free(snap_str);
4093 ao2_ref(snapshot, -1);
4094
4095 return 1;
4096}
4097
4098static int action_confbridgelist(struct mansession *s, const struct message *m)
4099{
4100 const char *actionid = astman_get_header(m, "ActionID");
4101 const char *conference_name = astman_get_header(m, "Conference");
4102 struct confbridge_user *user;
4103 struct confbridge_conference *conference;
4104 char id_text[80];
4105 int total = 0;
4106
4107 id_text[0] = '\0';
4108 if (!ast_strlen_zero(actionid)) {
4109 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
4110 }
4111 if (ast_strlen_zero(conference_name)) {
4112 astman_send_error(s, m, "No Conference name provided.");
4113 return 0;
4114 }
4116 astman_send_error(s, m, "No active conferences.");
4117 return 0;
4118 }
4119 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4120 if (!conference) {
4121 astman_send_error(s, m, "No Conference by that name found.");
4122 return 0;
4123 }
4124
4125 astman_send_listack(s, m, "Confbridge user list will follow", "start");
4126
4127 ao2_lock(conference);
4128 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4129 total += action_confbridgelist_item(s, id_text, conference, user, 0);
4130 }
4131 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
4132 total += action_confbridgelist_item(s, id_text, conference, user, 1);
4133 }
4134 ao2_unlock(conference);
4135 ao2_ref(conference, -1);
4136
4137 astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
4139
4140 return 0;
4141}
4142
4143static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
4144{
4145 const char *actionid = astman_get_header(m, "ActionID");
4146 struct confbridge_conference *conference;
4147 struct ao2_iterator iter;
4148 char id_text[512] = "";
4149 int totalitems = 0;
4150
4151 if (!ast_strlen_zero(actionid)) {
4152 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
4153 }
4154
4156 astman_send_error(s, m, "No active conferences.");
4157 return 0;
4158 }
4159
4160 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
4161
4162 /* Traverse the conference list */
4164 while ((conference = ao2_iterator_next(&iter))) {
4165 totalitems++;
4166
4167 ao2_lock(conference);
4168 astman_append(s,
4169 "Event: ConfbridgeListRooms\r\n"
4170 "%s"
4171 "Conference: %s\r\n"
4172 "Parties: %u\r\n"
4173 "Marked: %u\r\n"
4174 "Locked: %s\r\n"
4175 "Muted: %s\r\n"
4176 "\r\n",
4177 id_text,
4178 conference->name,
4179 conference->activeusers + conference->waitingusers,
4180 conference->markedusers,
4181 AST_YESNO(conference->locked),
4182 AST_YESNO(conference->muted));
4183 ao2_unlock(conference);
4184
4185 ao2_ref(conference, -1);
4186 }
4187 ao2_iterator_destroy(&iter);
4188
4189 /* Send final confirmation */
4190 astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
4192 return 0;
4193}
4194
4195static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
4196{
4197 const char *conference_name = astman_get_header(m, "Conference");
4198 const char *channel_name = astman_get_header(m, "Channel");
4199 int res = 0;
4200
4201 if (ast_strlen_zero(conference_name)) {
4202 astman_send_error(s, m, "No Conference name provided.");
4203 return 0;
4204 }
4205 if (ast_strlen_zero(channel_name)) {
4206 astman_send_error(s, m, "No channel name provided.");
4207 return 0;
4208 }
4210 astman_send_error(s, m, "No active conferences.");
4211 return 0;
4212 }
4213
4214 res = generic_mute_unmute_helper(mute, conference_name, channel_name);
4215
4216 if (res == -1) {
4217 astman_send_error(s, m, "No Conference by that name found.");
4218 return 0;
4219 } else if (res == -2) {
4220 astman_send_error(s, m, "No Channel by that name found in Conference.");
4221 return 0;
4222 }
4223
4224 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
4225 return 0;
4226}
4227
4228static int action_confbridgeunmute(struct mansession *s, const struct message *m)
4229{
4230 return action_mute_unmute_helper(s, m, 0);
4231}
4232static int action_confbridgemute(struct mansession *s, const struct message *m)
4233{
4234 return action_mute_unmute_helper(s, m, 1);
4235}
4236
4237static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
4238{
4239 const char *conference_name = astman_get_header(m, "Conference");
4240 int res = 0;
4241
4242 if (ast_strlen_zero(conference_name)) {
4243 astman_send_error(s, m, "No Conference name provided.");
4244 return 0;
4245 }
4247 astman_send_error(s, m, "No active conferences.");
4248 return 0;
4249 }
4250 if ((res = generic_lock_unlock_helper(lock, conference_name))) {
4251 astman_send_error(s, m, "No Conference by that name found.");
4252 return 0;
4253 }
4254 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
4255 return 0;
4256}
4257static int action_confbridgeunlock(struct mansession *s, const struct message *m)
4258{
4259 return action_lock_unlock_helper(s, m, 0);
4260}
4261static int action_confbridgelock(struct mansession *s, const struct message *m)
4262{
4263 return action_lock_unlock_helper(s, m, 1);
4264}
4265
4266static int action_confbridgekick(struct mansession *s, const struct message *m)
4267{
4268 const char *conference_name = astman_get_header(m, "Conference");
4269 const char *channel = astman_get_header(m, "Channel");
4270 struct confbridge_conference *conference;
4271 int found;
4272
4273 if (ast_strlen_zero(conference_name)) {
4274 astman_send_error(s, m, "No Conference name provided.");
4275 return 0;
4276 }
4278 astman_send_error(s, m, "No active conferences.");
4279 return 0;
4280 }
4281
4282 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4283 if (!conference) {
4284 astman_send_error(s, m, "No Conference by that name found.");
4285 return 0;
4286 }
4287
4288 found = !kick_conference_participant(conference, channel);
4289 ao2_ref(conference, -1);
4290
4291 if (found) {
4292 astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked");
4293 } else {
4294 astman_send_error(s, m, "No Channel by that name found in Conference.");
4295 }
4296 return 0;
4297}
4298
4299static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
4300{
4301 const char *conference_name = astman_get_header(m, "Conference");
4302 const char *recordfile = astman_get_header(m, "RecordFile");
4303 struct confbridge_conference *conference;
4304
4305 if (ast_strlen_zero(conference_name)) {
4306 astman_send_error(s, m, "No Conference name provided.");
4307 return 0;
4308 }
4310 astman_send_error(s, m, "No active conferences.");
4311 return 0;
4312 }
4313
4314 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4315 if (!conference) {
4316 astman_send_error(s, m, "No Conference by that name found.");
4317 return 0;
4318 }
4319
4320 ao2_lock(conference);
4321 if (conf_is_recording(conference)) {
4322 astman_send_error(s, m, "Conference is already being recorded.");
4323 ao2_unlock(conference);
4324 ao2_ref(conference, -1);
4325 return 0;
4326 }
4327
4328 if (!ast_strlen_zero(recordfile)) {
4329 ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
4330 }
4331
4332 if (conf_start_record(conference)) {
4333 astman_send_error(s, m, "Internal error starting conference recording.");
4334 ao2_unlock(conference);
4335 ao2_ref(conference, -1);
4336 return 0;
4337 }
4338 ao2_unlock(conference);
4339
4340 ao2_ref(conference, -1);
4341 astman_send_ack(s, m, "Conference Recording Started.");
4342 return 0;
4343}
4344static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
4345{
4346 const char *conference_name = astman_get_header(m, "Conference");
4347 struct confbridge_conference *conference;
4348
4349 if (ast_strlen_zero(conference_name)) {
4350 astman_send_error(s, m, "No Conference name provided.");
4351 return 0;
4352 }
4354 astman_send_error(s, m, "No active conferences.");
4355 return 0;
4356 }
4357
4358 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4359 if (!conference) {
4360 astman_send_error(s, m, "No Conference by that name found.");
4361 return 0;
4362 }
4363
4364 ao2_lock(conference);
4365 if (conf_stop_record(conference)) {
4366 ao2_unlock(conference);
4367 astman_send_error(s, m, "Internal error while stopping recording.");
4368 ao2_ref(conference, -1);
4369 return 0;
4370 }
4371 ao2_unlock(conference);
4372
4373 ao2_ref(conference, -1);
4374 astman_send_ack(s, m, "Conference Recording Stopped.");
4375 return 0;
4376}
4377
4378static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
4379{
4380 const char *conference_name = astman_get_header(m, "Conference");
4381 const char *channel = astman_get_header(m, "Channel");
4382 struct confbridge_user *user;
4383 struct confbridge_conference *conference;
4384
4385 if (ast_strlen_zero(conference_name)) {
4386 astman_send_error(s, m, "No Conference name provided.");
4387 return 0;
4388 }
4389 if (ast_strlen_zero(channel)) {
4390 astman_send_error(s, m, "No channel name provided.");
4391 return 0;
4392 }
4394 astman_send_error(s, m, "No active conferences.");
4395 return 0;
4396 }
4397
4398 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4399 if (!conference) {
4400 astman_send_error(s, m, "No Conference by that name found.");
4401 return 0;
4402 }
4403
4404 /* find channel and set as video src. */
4405 ao2_lock(conference);
4406 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4407 if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) {
4409 break;
4410 }
4411 }
4412 ao2_unlock(conference);
4413 ao2_ref(conference, -1);
4414
4415 /* do not access user after conference unlock. We are just
4416 * using this check to see if it was found or not */
4417 if (!user) {
4418 astman_send_error(s, m, "No channel by that name found in conference.");
4419 return 0;
4420 }
4421 astman_send_ack(s, m, "Conference single video source set.");
4422 return 0;
4423}
4424
4425static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4426{
4427 char *parse;
4428 struct confbridge_conference *conference;
4429 struct confbridge_user *user;
4430 int count = 0;
4433 AST_APP_ARG(confno);
4434 );
4435
4436 /* parse all the required arguments and make sure they exist. */
4437 if (ast_strlen_zero(data)) {
4438 return -1;
4439 }
4440 parse = ast_strdupa(data);
4442 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
4443 return -1;
4444 }
4446 if (!conference) {
4447 snprintf(buf, len, "0");
4448 return 0;
4449 }
4450
4451 /* get the correct count for the type requested */
4453 if (!strcasecmp(args.type, "parties")) {
4455 count++;
4456 }
4458 count++;
4459 }
4460 } else if (!strcasecmp(args.type, "admins")) {
4462 if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
4463 count++;
4464 }
4465 }
4466 } else if (!strcasecmp(args.type, "marked")) {
4468 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
4469 count++;
4470 }
4471 }
4472 } else if (!strcasecmp(args.type, "locked")) {
4473 count = conference->locked;
4474 } else if (!strcasecmp(args.type, "muted")) {
4475 count = conference->muted;
4476 } else {
4477 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.\n", args.type);
4478 }
4479 snprintf(buf, len, "%d", count);
4481 ao2_ref(conference, -1);
4482 return 0;
4483}
4484
4485static int confkick_exec(struct ast_channel *chan, const char *data)
4486{
4487 char *parse;
4488 struct confbridge_conference *conference;
4489 int not_found;
4490
4492 AST_APP_ARG(confbridge);
4493 AST_APP_ARG(channel);
4494 );
4495
4496 if (ast_strlen_zero(data)) {
4497 ast_log(LOG_WARNING, "No conference bridge specified.\n");
4498 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4499 return 0;
4500 }
4501
4502 parse = ast_strdupa(data);
4504
4505 conference = ao2_find(conference_bridges, args.confbridge, OBJ_KEY);
4506 if (!conference) {
4507 ast_log(LOG_WARNING, "No conference bridge named '%s' found!\n", args.confbridge);
4508 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4509 return 0;
4510 }
4511 if (ast_strlen_zero(args.channel)) {
4512 not_found = kick_conference_participant(conference, "all");
4513 } else {
4514 not_found = kick_conference_participant(conference, args.channel);
4515 }
4516
4517 ao2_ref(conference, -1);
4518 if (not_found) {
4519 if (ast_strlen_zero(args.channel) || !strcasecmp("all", args.channel) || !strcasecmp("participants", args.channel)) {
4520 ast_log(LOG_WARNING, "No participants found in conference bridge '%s'!\n", args.confbridge);
4521 } else {
4522 ast_log(LOG_WARNING, "No participant named '%s' found in conference bridge '%s'!\n", args.channel, args.confbridge);
4523 }
4524 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4525 return 0;
4526 }
4527 ast_debug(1, "Kicked '%s' out of conference '%s'\n", args.channel, args.confbridge);
4528 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "SUCCESS");
4529 return 0;
4530}
4531
4533{
4534 AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
4535 conference->activeusers++;
4536}
4537
4539{
4540 AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
4541 conference->activeusers++;
4542 conference->markedusers++;
4543}
4544
4546{
4547 AST_LIST_INSERT_TAIL(&conference->waiting_list, user, list);
4548 conference->waitingusers++;
4549}
4550
4552{
4553 AST_LIST_REMOVE(&conference->active_list, user, list);
4554 conference->activeusers--;
4555}
4556
4558{
4559 AST_LIST_REMOVE(&conference->active_list, user, list);
4560 conference->activeusers--;
4561 conference->markedusers--;
4562}
4563
4565{
4567
4568 /* Turn on MOH if the single participant is set up for it */
4569 if (ast_test_flag(&only_user->u_profile, USER_OPT_MUSICONHOLD)) {
4570 conf_moh_start(only_user);
4571 }
4572 conf_update_user_mute(only_user);
4573}
4574
4576{
4579}
4580
4581/*!
4582 * \internal
4583 * \brief Unregister a ConfBridge channel technology.
4584 * \since 12.0.0
4585 *
4586 * \param tech What to unregister.
4587 */
4589{
4592}
4593
4594/*!
4595 * \internal
4596 * \brief Register a ConfBridge channel technology.
4597 * \since 12.0.0
4598 *
4599 * \param tech What to register.
4600 *
4601 * \retval 0 on success.
4602 * \retval -1 on error.
4603 */
4605{
4607 if (!tech->capabilities) {
4608 return -1;
4609 }
4611 if (ast_channel_register(tech)) {
4612 ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
4613 tech->type, tech->description);
4614 return -1;
4615 }
4616 return 0;
4617}
4618
4619/*! \brief Called when module is being unloaded */
4620static int unload_module(void)
4621{
4624
4628
4630
4631 ast_manager_unregister("ConfbridgeList");
4632 ast_manager_unregister("ConfbridgeListRooms");
4633 ast_manager_unregister("ConfbridgeMute");
4634 ast_manager_unregister("ConfbridgeUnmute");
4635 ast_manager_unregister("ConfbridgeKick");
4636 ast_manager_unregister("ConfbridgeUnlock");
4637 ast_manager_unregister("ConfbridgeLock");
4638 ast_manager_unregister("ConfbridgeStartRecord");
4639 ast_manager_unregister("ConfbridgeStopRecord");
4640 ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
4641
4642 /* Unsubscribe from stasis confbridge message type and clean it up. */
4644
4645 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
4648
4650
4653
4654 return 0;
4655}
4656
4657/*!
4658 * \brief Load the module
4659 *
4660 * Module loading including tests for configuration or dependencies.
4661 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
4662 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
4663 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
4664 * configuration file or other non-critical problem return
4665 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
4666 */
4667static int load_module(void)
4668{
4669 int res = 0;
4670
4671 if (conf_load_config()) {
4672 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
4674 }
4675
4678 unload_module();
4680 }
4681
4682 /* Create a container to hold the conference bridges */
4686 if (!conference_bridges) {
4687 unload_module();
4689 }
4690
4691 /* Setup manager stasis subscriptions */
4692 res |= manager_confbridge_init();
4693
4696
4700
4702
4712 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
4713 if (res) {
4714 unload_module();
4716 }
4717
4719}
4720
4721static int reload(void)
4722{
4723 return conf_reload_config();
4724}
4725
4726AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
4727 .support_level = AST_MODULE_SUPPORT_CORE,
4728 .load = load_module,
4729 .unload = unload_module,
4730 .reload = reload,
4731 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
4732 .optional_modules = "codec_speex,func_jitterbuffer",
static int quiet
Definition: ael_main.c:123
char digit
static void playback_common(struct confbridge_conference *conference, const char *filename, int say_number)
int conf_handle_only_person(struct confbridge_user *user)
Handle actions whenever an user joins an empty conference.
static void playback_task_data_init(struct playback_task_data *ptd, struct confbridge_conference *conference, const char *filename, int say_number)
static char * handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int action_confbridgemute(struct mansession *s, const struct message *m)
static int push_announcer(struct confbridge_conference *conference)
Push the announcer channel into the bridge.
void conf_moh_stop(struct confbridge_user *user)
Stop MOH for the conference user.
static void send_conf_start_event(struct confbridge_conference *conference)
static struct ast_datastore_info async_datastore_info
Datastore used for timing of async announcement playback.
static int send_event_hook_callback(struct ast_bridge_channel *bridge_channel, void *data)
static int generic_lock_unlock_helper(int lock, const char *conference_name)
static int action_confbridgeunlock(struct mansession *s, const struct message *m)
static int join_callback(struct ast_bridge_channel *bridge_channel, void *ignore)
void conf_add_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
Add a conference bridge user as an unmarked active user of the conference.
static const char app[]
static int action_confbridgekick(struct mansession *s, const struct message *m)
static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
static void hangup_data_init(struct hangup_data *hangup, struct confbridge_conference *conference)
static int register_channel_tech(struct ast_channel_tech *tech)
static void unregister_channel_tech(struct ast_channel_tech *tech)
static void conf_moh_unsuspend(struct confbridge_user *user)
static char * handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
const char * conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
Looks to see if sound file is stored in bridge profile sounds, if not default sound is provided.
static int action_playback_and_continue(struct confbridge_conference *conference, struct confbridge_user *user, struct ast_bridge_channel *bridge_channel, struct conf_menu *menu, const char *playback_file, const char *cur_dtmf, int *stop_prompts)
int conf_handle_inactive_waitmarked(struct confbridge_user *user)
Handle actions every time a waitmarked user joins w/o a marked user present.
static int action_confbridgeunmute(struct mansession *s, const struct message *m)
static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
void confbridge_handle_atxfer(struct ast_attended_transfer_message *msg)
Create join/leave events for attended transfers.
void conf_add_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
Add a conference bridge user as a marked active user of the conference.
static struct ast_custom_function confbridge_function
static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
static void playback_task_data_destroy(struct playback_task_data *ptd)
static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
static int handle_conf_user_leave(struct confbridge_user *user)
Call the proper leave event handler for the user for the conference bridge's current state.
static int action_toggle_mute(struct confbridge_conference *conference, struct confbridge_user *user, struct ast_bridge_channel *bridge_channel)
static void send_join_event(struct confbridge_user *user, struct confbridge_conference *conference)
static char * handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void send_stop_record_event(struct confbridge_conference *conference)
static struct async_datastore_data * async_datastore_data_alloc(void)
static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static void send_mute_event(struct confbridge_user *user, struct confbridge_conference *conference)
#define CONFERENCE_BRIDGE_BUCKETS
static void leave_conference(struct confbridge_user *user)
Leave a conference.
void conf_add_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
Add a conference bridge user as an waiting user of the conference.
static int conf_stop_record(struct confbridge_conference *conference)
static int action_confbridgelist(struct mansession *s, const struct message *m)
void conf_remove_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user)
Remove a conference bridge user from the waiting conference users in the conference.
static char * complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
static int func_confbridge_channels(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked)
static int kick_conference_participant(struct confbridge_conference *conference, const char *channel)
static const char app2[]
void conf_ended(struct confbridge_conference *conference)
Callback to be called when the conference has become empty.
static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
Comparison function used for conference bridges container.
static int action_toggle_mute_participants(struct confbridge_conference *conference, struct confbridge_user *user)
static struct ast_custom_function confbridge_info_function
static void async_delete_name_rec_task_data_destroy(struct async_delete_name_rec_task_data *atd)
static struct confbridge_conference * join_conference_bridge(const char *conference_name, struct confbridge_user *user)
Join a conference bridge.
static char * complete_confbridge_name(const char *line, const char *word, int pos, int state)
int async_play_sound_file(struct confbridge_conference *conference, const char *filename, struct ast_channel *initiator)
Play sound file into conference bridge asynchronously.
static int play_sound_number(struct confbridge_conference *conference, int say_number)
Play number into the conference bridge.
int play_sound_file(struct confbridge_conference *conference, const char *filename)
Play sound file into conference bridge.
static void async_datastore_data_destroy(void *data)
static int confbridge_exec(struct ast_channel *chan, const char *data)
The ConfBridge application.
static int conf_start_record(struct confbridge_conference *conference)
static char * handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void send_leave_event(struct confbridge_user *user, struct confbridge_conference *conference)
void conf_remove_user_marked(struct confbridge_conference *conference, struct confbridge_user *user)
Remove a conference bridge user from the marked active conference users in the conference.
static void async_playback_task_data_destroy(struct async_playback_task_data *aptd)
static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number)
int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct confbridge_user *user, struct conf_menu_entry *menu_entry, struct conf_menu *menu)
Once a DTMF sequence matches a sequence in the user's DTMF menu, this function will get called to per...
static struct async_playback_task_data * async_playback_task_data_alloc(struct confbridge_conference *conference, const char *filename, int say_number, struct ast_channel *initiator)
static void conf_moh_suspend(struct confbridge_user *user)
static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
static int playback_task(void *data)
Play an announcement into a confbridge.
void conf_update_user_mute(struct confbridge_user *user)
Update the actual mute status of the user and set it on the bridge.
static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user, struct ast_bridge_channel *bridge_channel)
Announce number of users in the conference bridge to the caller.
static void destroy_conference_bridge(void *obj)
Destroy a conference bridge.
static int async_delete_name_rec(struct confbridge_conference *conference, const char *filename)
static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
static int action_kick_last(struct confbridge_conference *conference, struct ast_bridge_channel *bridge_channel, struct confbridge_user *user)
static int action_toggle_binaural(struct confbridge_conference *conference, struct confbridge_user *user, struct ast_bridge_channel *bridge_channel)
static int async_delete_name_rec_task(void *data)
Delete user's name file asynchronously.
static int handle_conf_user_join(struct confbridge_user *user)
Call the proper join event handler for the user for the conference bridge's current state.
static void send_unmute_event(struct confbridge_user *user, struct confbridge_conference *conference)
static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
static int conference_bridge_hash_cb(const void *obj, const int flags)
Hashing function used for conference bridges container.
static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
void conf_mute_only_active(struct confbridge_conference *conference)
Attempt to mute/play MOH to the only user in the conference if they require it.
struct ao2_container * conference_bridges
Container to hold all conference bridges in progress.
int conf_add_post_join_action(struct confbridge_user *user, int(*func)(struct confbridge_user *user))
Queue a function to run with the given conference bridge user as an argument once the state transitio...
static int async_play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number, struct ast_channel *initiator)
static void generic_mute_unmute_user(struct confbridge_conference *conference, struct confbridge_user *user, int mute)
struct confbridge_conference * conf_find_bridge(const char *conference_name)
Find a confbridge by name.
static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking)
void conf_handle_first_join(struct confbridge_conference *conference)
Callback to execute any time we transition from zero to one active users.
static char * handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
void conf_moh_start(struct confbridge_user *user)
Start MOH for the conference user.
static int load_module(void)
Load the module.
static void handle_video_on_exit(struct confbridge_conference *conference, struct ast_channel *chan)
static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel, const char *filename)
Playback the given filename and monitor for any dtmf interrupts.
static int generic_mute_unmute_helper(int mute, const char *conference_name, const char *chan_name)
static void hangup_data_destroy(struct hangup_data *hangup)
static int hangup_playback(void *data)
Hang up the announcer channel.
static int unload_module(void)
Called when module is being unloaded.
static int reload(void)
static int action_confbridgelock(struct mansession *s, const struct message *m)
static int conf_is_recording(struct confbridge_conference *conference)
static struct ast_custom_function confbridge_channels_function
static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
Play back an audio file to a channel.
static int sound_file_exists(const char *filename)
static int execute_menu_entry(struct confbridge_conference *conference, struct confbridge_user *user, struct ast_bridge_channel *bridge_channel, struct conf_menu_entry *menu_entry, struct conf_menu *menu)
static int action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
#define RECORD_FILENAME_INITIAL_SPACE
static int alloc_playback_chan(struct confbridge_conference *conference)
void conf_remove_user_active(struct confbridge_conference *conference, struct confbridge_user *user)
Remove a conference bridge user from the unmarked active conference users in the conference.
void async_play_sound_ready(struct ast_channel *chan)
Indicate the initiator of an async sound file is ready for it to play.
static int setup_async_playback_datastore(struct ast_channel *initiator)
Prepare the async playback datastore.
static char * handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static struct async_delete_name_rec_task_data * async_delete_name_rec_task_data_alloc(struct confbridge_conference *conference, const char *filename)
static void send_conf_stasis_snapshots(struct confbridge_conference *conference, struct ast_channel_snapshot *chan_snapshot, struct stasis_message_type *type, struct ast_json *extras)
static void send_start_record_event(struct confbridge_conference *conference)
static int async_playback_task(void *data)
Play an announcement into a confbridge asynchronously.
static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
static struct ast_cli_entry cli_confbridge[]
void conf_handle_second_active(struct confbridge_conference *conference)
Handle when a conference moves to having more than one active participant.
static void confbridge_unlock_and_unref(void *obj)
static void send_conf_end_event(struct confbridge_conference *conference)
static void wait_for_initiator(struct ast_channel *initiator)
Wait for the initiator of an async playback to be ready.
static char * handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *extras, int channel_topic)
static int confkick_exec(struct ast_channel *chan, const char *data)
static char * handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define MAX_PIN
Definition: app_meetme.c:882
ast_mutex_t lock
Definition: app_sla.c:337
Asterisk main include file. File version handling, generic pbx functions.
int ast_shutting_down(void)
Definition: asterisk.c:1883
#define PATH_MAX
Definition: asterisk.h:40
#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
void ast_free_ptr(void *ptr)
free() wrapper
Definition: astmm.c:1739
#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_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
#define OBJ_KEY
Definition: astobj2.h:1151
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
Audiohooks Architecture.
@ AST_AUDIOHOOK_DIRECTION_READ
Definition: audiohook.h:49
@ AST_AUDIOHOOK_DIRECTION_WRITE
Definition: audiohook.h:50
int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
Retrieve the volume adjustment value on frames read from or written to a channel.
Definition: audiohook.c:1346
int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
Adjust the volume on frames read from or written to a channel.
Definition: audiohook.c:1371
int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
Adjust the volume on frames read from or written to a channel.
Definition: audiohook.c:1321
Bridging API.
void ast_bridge_set_remb_estimated_bitrate(struct ast_bridge *bridge, float estimated_bitrate)
Force the REMB report estimated bitrate to a specific max value.
Definition: bridge.c:3876
void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
Set the bridge to pick the strongest talker supporting video as the single source video feed.
Definition: bridge.c:3835
struct ast_bridge * ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
Create a new base class bridge.
Definition: bridge.c:999
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:485
int ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, struct ast_bridge_tech_optimizations *tech_args, enum ast_bridge_join_flags flags)
Join a channel to a bridge (blocking)
Definition: bridge.c:1690
void ast_bridge_set_binaural_active(struct ast_bridge *bridge, unsigned int binaural_active)
Activates the use of binaural signals in a conference bridge.
Definition: bridge.c:3774
@ AST_BRIDGE_VIDEO_SFU_REMB_LOWEST
Definition: bridge.h:139
@ AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST_ALL
Definition: bridge.h:147
@ AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE
Definition: bridge.h:137
@ AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST
Definition: bridge.h:141
@ AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE_ALL
Definition: bridge.h:143
@ AST_BRIDGE_VIDEO_SFU_REMB_FORCE
Definition: bridge.h:149
@ AST_BRIDGE_VIDEO_SFU_REMB_LOWEST_ALL
Definition: bridge.h:145
void ast_bridge_set_sfu_video_mode(struct ast_bridge *bridge)
Set the bridge to be a selective forwarding unit.
Definition: bridge.c:3843
void ast_bridge_set_maximum_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
Adjust the maximum mixing sample rate of a bridge used during multimix mode.
Definition: bridge.c:3788
void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int video_update_discard)
Set the amount of time to discard subsequent video updates after a video update has been sent.
Definition: bridge.c:3851
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:1009
int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, enum ast_bridge_impart_flags flags) attribute_warn_unused_result
Impart a channel to a bridge (non-blocking)
Definition: bridge.c:1947
void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior)
Set the REMB report generation behavior on a bridge.
Definition: bridge.c:3867
int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
Remove a channel from a bridge.
Definition: bridge.c:2020
@ AST_BRIDGE_CAPABILITY_MULTIMIX
Definition: bridge.h:98
int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
Suspend a channel temporarily from a bridge.
Definition: bridge.c:3076
void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
Set a bridge to feed a single video source to all participants.
Definition: bridge.c:3818
void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixing_interval)
Adjust the internal mixing interval of a bridge used during multimix mode.
Definition: bridge.c:3767
void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
remove a channel as a source of video for the bridge.
Definition: bridge.c:3986
int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
Unsuspend a channel from a bridge.
Definition: bridge.c:3097
void ast_bridge_set_send_sdp_label(struct ast_bridge *bridge, unsigned int send_sdp_label)
Controls whether to send a "label" attribute in each stream in an SDP.
Definition: bridge.c:4035
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:474
@ AST_BRIDGE_IMPART_CHAN_INDEPENDENT
Definition: bridge.h:594
int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
Determine if a channel is a video src for the bridge.
Definition: bridge.c:3960
void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval)
Set the interval at which a combined REMB frame will be sent to video sources.
Definition: bridge.c:3858
void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
Adjust the internal mixing sample rate of a bridge used during multimix mode.
Definition: bridge.c:3781
void ast_bridge_channel_feature_digit_add(struct ast_bridge_channel *bridge_channel, int digit)
Add a DTMF digit to the collected digits.
void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
Lock the bridge associated with the bridge channel.
@ BRIDGE_CHANNEL_STATE_END
void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
Set bridge channel state to leave bridge (if not leaving already).
ast_bridge_hook_type
@ AST_BRIDGE_HOOK_TYPE_JOIN
@ AST_BRIDGE_HOOK_TYPE_LEAVE
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3689
int ast_bridge_interval_hook(struct ast_bridge_features *features, enum ast_bridge_hook_timer_option flags, unsigned int interval, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach an interval hook to a bridge features structure.
Definition: bridge.c:3388
@ AST_BRIDGE_HOOK_REMOVE_ON_PULL
int ast_bridge_talk_detector_hook(struct ast_bridge_features *features, ast_bridge_talking_indicate_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel talk detection hook to a bridge features structure.
Definition: bridge.c:3364
int ast_bridge_join_hook(struct ast_bridge_features *features, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel join hook to a bridge features structure.
Definition: bridge.c:3344
struct ast_bridge_features * ast_bridge_features_new(void)
Allocate a new bridge features struct.
Definition: bridge.c:3752
@ AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY
@ AST_BRIDGE_FLAG_MASQUERADE_ONLY
#define MAXIMUM_DTMF_FEATURE_STRING
Maximum length of a DTMF feature string.
int ast_bridge_leave_hook(struct ast_bridge_features *features, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach a bridge channel leave hook to a bridge features structure.
Definition: bridge.c:3354
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3722
void ast_bridge_features_destroy(struct ast_bridge_features *features)
Destroy an allocated bridge features struct.
Definition: bridge.c:3743
@ AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE
static int priority
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2387
static const char type[]
Definition: chan_ooh323.c:109
static int hangup(void *data)
Definition: chan_pjsip.c:2520
General Asterisk PBX channel definitions.
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2414
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2570
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
#define ast_channel_lock(chan)
Definition: channel.h:2970
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition: channel.c:570
int ast_channel_priority(const struct ast_channel *chan)
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2995
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f)
Queue one or more frames to a channel's frame queue.
Definition: channel.c:1158
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
Definition: channel.c:539
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3006
int ast_channel_get_up_time(struct ast_channel *chan)
Obtain how long it has been since the channel was answered.
Definition: channel.c:2874
const char * ast_channel_language(const struct ast_channel *chan)
void ast_channel_context_set(struct ast_channel *chan, const char *value)
struct ast_pbx * ast_channel_pbx(const struct ast_channel *chan)
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_channel_priority_set(struct ast_channel *chan, int value)
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:3017
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2834
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6371
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2971
void ast_channel_pbx_set(struct ast_channel *chan, struct ast_pbx *value)
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2428
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
Definition: cli.h:71
#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
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
@ AST_MEDIA_TYPE_UNKNOWN
Definition: codec.h:31
short word
struct ast_channel_tech * conf_announce_get_tech(void)
Get ConfBridge announce channel technology struct.
int conf_announce_channel_push(struct ast_channel *ast)
Push the announcer channel into the conference.
struct ast_channel_tech * conf_record_get_tech(void)
Get ConfBridge record channel technology struct.
int func_confbridge_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
int conf_reload_config(void)
reload confbridge.conf file
int conf_find_menu_entry_by_sequence(const char *dtmf_sequence, struct conf_menu *menu, struct conf_menu_entry *result)
Finds a menu_entry in a menu structure matched by DTMF sequence.
void conf_bridge_profile_destroy(struct bridge_profile *b_profile)
Destroy a bridge profile found by 'conf_find_bridge_profile'.
int conf_load_config(void)
load confbridge.conf file
const struct bridge_profile * conf_find_bridge_profile(struct ast_channel *chan, const char *bridge_profile_name, struct bridge_profile *result)
Find a bridge profile given a bridge profile's name and store that profile in result structure.
int conf_set_menu_to_user(struct ast_channel *chan, struct confbridge_user *user, const char *menu_profile_name)
find a menu profile given a menu profile's name and apply the menu in DTMF hooks.
void conf_bridge_profile_copy(struct bridge_profile *dst, struct bridge_profile *src)
copies a bridge profile
void conf_destroy_config(void)
destroy the information loaded from the confbridge.conf file
const struct user_profile * conf_find_user_profile(struct ast_channel *chan, const char *user_profile_name, struct user_profile *result)
find a user profile given a user profile's name and store that profile in result structure.
void conf_menu_entry_destroy(struct conf_menu_entry *menu_entry)
Destroys and frees all the actions stored in a menu_entry structure.
void conf_invalid_event_fn(struct confbridge_user *user)
A handler for join/leave events that are invalid in a particular state.
Definition: conf_state.c:42
void(* conference_event_fn)(struct confbridge_user *user)
Definition: conf_state.h:40
struct confbridge_state * CONF_STATE_EMPTY
Conference state with no active or waiting users.
struct stasis_message_type * confbridge_mute_type(void)
get the confbridge mute stasis message type
#define DEFAULT_SILENCE_THRESHOLD
Definition: confbridge.h:49
struct stasis_message_type * confbridge_talking_type(void)
get the confbridge talking stasis message type
struct stasis_message_type * confbridge_stop_record_type(void)
get the confbridge stop_record stasis message type
@ USER_OPT_ENDMARKED
Definition: confbridge.h:60
@ USER_OPT_WAITMARKED
Definition: confbridge.h:59
@ USER_OPT_ANNOUNCEUSERCOUNTALL
Definition: confbridge.h:66
@ USER_OPT_HEAR_OWN_JOIN_SOUND
Definition: confbridge.h:73
@ USER_OPT_TEXT_MESSAGING
Definition: confbridge.h:71
@ USER_OPT_QUIET
Definition: confbridge.h:57
@ USER_OPT_DENOISE
Definition: confbridge.h:61
@ USER_OPT_MARKEDUSER
Definition: confbridge.h:54
@ USER_OPT_DTMF_PASS
Definition: confbridge.h:65
@ USER_OPT_NOONLYPERSON
Definition: confbridge.h:53
@ USER_OPT_TALKER_DETECT
Definition: confbridge.h:63
@ USER_OPT_MUSICONHOLD
Definition: confbridge.h:56
@ USER_OPT_ANNOUNCEUSERCOUNT
Definition: confbridge.h:58
@ USER_OPT_STARTMUTED
Definition: confbridge.h:55
@ USER_OPT_DROP_SILENCE
Definition: confbridge.h:64
@ USER_OPT_ANSWER_CHANNEL
Definition: confbridge.h:72
@ USER_OPT_ENDMARKEDANY
Definition: confbridge.h:74
@ USER_OPT_ADMIN
Definition: confbridge.h:52
@ USER_OPT_JITTERBUFFER
Definition: confbridge.h:67
@ USER_OPT_ANNOUNCE_JOIN_LEAVE
Definition: confbridge.h:62
@ USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW
Definition: confbridge.h:68
struct stasis_message_type * confbridge_end_type(void)
get the confbridge end stasis message type
conf_sounds
Definition: confbridge.h:169
@ CONF_SOUND_JOIN
Definition: confbridge.h:188
@ CONF_SOUND_OTHER_IN_PARTY
Definition: confbridge.h:177
@ CONF_SOUND_INVALID_PIN
Definition: confbridge.h:182
@ CONF_SOUND_LOCKED_NOW
Definition: confbridge.h:185
@ CONF_SOUND_THERE_ARE
Definition: confbridge.h:176
@ CONF_SOUND_ONLY_ONE
Definition: confbridge.h:175
@ CONF_SOUND_GET_PIN
Definition: confbridge.h:181
@ CONF_SOUND_ERROR_MENU
Definition: confbridge.h:187
@ CONF_SOUND_PARTICIPANTS_UNMUTED
Definition: confbridge.h:191
@ CONF_SOUND_BINAURAL_ON
Definition: confbridge.h:193
@ CONF_SOUND_ONLY_PERSON
Definition: confbridge.h:183
@ CONF_SOUND_BEGIN
Definition: confbridge.h:192
@ CONF_SOUND_PARTICIPANTS_MUTED
Definition: confbridge.h:190
@ CONF_SOUND_WAIT_FOR_LEADER
Definition: confbridge.h:179
@ CONF_SOUND_HAS_LEFT
Definition: confbridge.h:171
@ CONF_SOUND_BINAURAL_OFF
Definition: confbridge.h:194
@ CONF_SOUND_LEAVE
Definition: confbridge.h:189
@ CONF_SOUND_LEADER_HAS_LEFT
Definition: confbridge.h:180
@ CONF_SOUND_PLACE_IN_CONF
Definition: confbridge.h:178
@ CONF_SOUND_HAS_JOINED
Definition: confbridge.h:170
@ CONF_SOUND_UNLOCKED_NOW
Definition: confbridge.h:186
@ CONF_SOUND_UNMUTED
Definition: confbridge.h:174
@ CONF_SOUND_MUTED
Definition: confbridge.h:173
@ CONF_SOUND_LOCKED
Definition: confbridge.h:184
@ CONF_SOUND_KICKED
Definition: confbridge.h:172
struct stasis_message_type * confbridge_start_type(void)
get the confbridge start stasis message type
#define DEFAULT_TALKING_THRESHOLD
Definition: confbridge.h:46
#define DEFAULT_MENU_PROFILE
Definition: confbridge.h:43
struct stasis_message_type * confbridge_start_record_type(void)
get the confbridge start_record stasis message type
#define DEFAULT_BRIDGE_PROFILE
Definition: confbridge.h:42
#define DEFAULT_USER_PROFILE
Definition: confbridge.h:41
struct stasis_message_type * confbridge_unmute_type(void)
get the confbridge unmute stasis message type
struct stasis_message_type * confbridge_join_type(void)
get the confbridge join stasis message type
@ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE
Definition: confbridge.h:86
@ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST_ALL
Definition: confbridge.h:92
@ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST
Definition: confbridge.h:87
@ BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER
Definition: confbridge.h:81
@ BRIDGE_OPT_VIDEO_SRC_LAST_MARKED
Definition: confbridge.h:79
@ BRIDGE_OPT_VIDEO_SRC_SFU
Definition: confbridge.h:85
@ BRIDGE_OPT_BINAURAL_ACTIVE
Definition: confbridge.h:84
@ BRIDGE_OPT_RECORD_CONFERENCE
Definition: confbridge.h:78
@ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST_ALL
Definition: confbridge.h:91
@ BRIDGE_OPT_RECORD_FILE_TIMESTAMP
Definition: confbridge.h:83
@ BRIDGE_OPT_ENABLE_EVENTS
Definition: confbridge.h:89
@ BRIDGE_OPT_REMB_BEHAVIOR_FORCE
Definition: confbridge.h:93
@ BRIDGE_OPT_RECORD_FILE_APPEND
Definition: confbridge.h:82
@ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE_ALL
Definition: confbridge.h:90
@ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST
Definition: confbridge.h:88
@ BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED
Definition: confbridge.h:80
struct stasis_message_type * confbridge_leave_type(void)
get the confbridge leave stasis message type
#define MAX_CONF_NAME
Definition: confbridge.h:35
@ MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC
Definition: confbridge.h:112
@ MENU_ACTION_SET_SINGLE_VIDEO_SRC
Definition: confbridge.h:111
@ MENU_ACTION_ADMIN_KICK_LAST
Definition: confbridge.h:108
@ MENU_ACTION_TOGGLE_BINAURAL
Definition: confbridge.h:115
@ MENU_ACTION_PLAYBACK
Definition: confbridge.h:98
@ MENU_ACTION_LEAVE
Definition: confbridge.h:109
@ MENU_ACTION_RESET_LISTENING
Definition: confbridge.h:102
@ MENU_ACTION_INCREASE_TALKING
Definition: confbridge.h:104
@ MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS
Definition: confbridge.h:114
@ MENU_ACTION_INCREASE_LISTENING
Definition: confbridge.h:100
@ MENU_ACTION_DIALPLAN_EXEC
Definition: confbridge.h:106
@ MENU_ACTION_DECREASE_LISTENING
Definition: confbridge.h:101
@ MENU_ACTION_PLAYBACK_AND_CONTINUE
Definition: confbridge.h:99
@ MENU_ACTION_ADMIN_TOGGLE_LOCK
Definition: confbridge.h:107
@ MENU_ACTION_DECREASE_TALKING
Definition: confbridge.h:105
@ MENU_ACTION_PARTICIPANT_COUNT
Definition: confbridge.h:113
@ MENU_ACTION_TOGGLE_MUTE
Definition: confbridge.h:97
@ MENU_ACTION_NOOP
Definition: confbridge.h:110
@ MENU_ACTION_RESET_TALKING
Definition: confbridge.h:103
int manager_confbridge_init(void)
register stasis message routers to handle manager events for confbridge messages
void conf_send_event_to_participants(struct confbridge_conference *conference, struct ast_channel *chan, struct stasis_message *msg)
Send events to bridge participants.
void manager_confbridge_shutdown(void)
unregister stasis message routers to handle manager events for confbridge messages
#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
@ AST_DEVSTATE_CACHABLE
Definition: devicestate.h:70
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:513
@ AST_DEVICE_INUSE
Definition: devicestate.h:55
@ THRESHOLD_SILENCE
Definition: dsp.h:73
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
@ E_MATCH
Definition: extconf.h:217
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1301
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1886
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1137
#define AST_DIGIT_NONE
Definition: file.h:47
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1149
#define AST_DIGIT_ANY
Definition: file.h:48
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1848
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type)
Add all codecs Asterisk knows about for a specific type to the capabilities structure.
Definition: format_cap.c:216
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
static const char name[]
Definition: format_mp3.c:68
static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_logic.c:266
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:2028
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1986
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:2064
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2018
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1647
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2072
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1907
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7697
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object,...
const char * ext
Definition: http.c:150
#define AST_APP_ARG(name)
Define an application argument.
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:188
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path)
Record a file based on input from a channel. Use default accept and cancel DTMF. This function will p...
Definition: main/app.c:2154
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
Allow to record message and have a review option.
Definition: main/app.c:2646
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
@ AST_FRAME_CONTROL
@ AST_CONTROL_HANGUP
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define LOG_WARNING
Asterisk JSON abstraction layer.
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
int ast_json_object_update(struct ast_json *object, struct ast_json *other)
Update object with all of the fields of other.
Definition: json.c:426
#define AST_LIST_LAST(head)
Returns the last entry contained in a list.
Definition: linkedlists.h:429
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
Asterisk locking-related definitions:
#define ast_cond_destroy(cond)
Definition: lock.h:206
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:608
#define ast_cond_wait(cond, mutex)
Definition: lock.h:209
#define ast_cond_init(cond, attr)
Definition: lock.h:205
#define ast_mutex_init(pmutex)
Definition: lock.h:190
#define ast_mutex_unlock(a)
Definition: lock.h:194
pthread_cond_t ast_cond_t
Definition: lock.h:182
#define ast_mutex_destroy(a)
Definition: lock.h:192
#define ast_mutex_lock(a)
Definition: lock.h:193
#define ast_cond_signal(cond)
Definition: lock.h:207
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_REPORTING
Definition: manager.h:84
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
#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
Out-of-call text message support.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_DEVSTATE_PROVIDER
Definition: module.h:343
@ 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
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Music on hold handling.
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7787
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7797
def ignore(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:48
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
Core PBX routines and definitions.
int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid, const char *application, void *data, void(*datad)(void *), const char *registrar)
Add and extension to an extension context.
Definition: pbx.c:6943
enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
Execute the PBX in the current thread.
Definition: pbx.c:4750
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4190
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
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.
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
Simply remove extension from context.
Definition: pbx.c:4963
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1568
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
struct ast_exten * pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, struct pbx_find_info *q, const char *context, const char *exten, int priority, const char *label, const char *callerid, enum ext_match_t action)
Definition: ael_main.c:152
@ AST_CFE_WRITE
Definition: pbx.h:1552
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
static int total
Definition: res_adsi.c:970
static char user[512]
#define NULL
Definition: resample.c:96
Say numbers and dates (maybe words one day too)
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8261
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1538
struct stasis_message * ast_bridge_blob_create(struct stasis_message_type *type, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_json *blob)
Creates a ast_bridge_blob message.
struct ast_bridge_snapshot * ast_bridge_snapshot_create(struct ast_bridge *bridge)
Generate a snapshot of the bridge state. This is an ao2 object, so ao2_cleanup() to deallocate.
struct stasis_topic * ast_bridge_topic(struct ast_bridge *bridge)
A topic which publishes the events for a particular bridge.
struct stasis_message * ast_bridge_blob_create_from_snapshots(struct stasis_message_type *type, struct ast_bridge_snapshot *bridge_snapshot, struct ast_channel_snapshot *chan_snapshot, struct ast_json *blob)
Creates a ast_bridge_blob message from snapshots.
Media Stream API.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#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
#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
@ AST_STRSEP_TRIM
Definition: strings.h:256
@ AST_STRSEP_STRIP
Definition: strings.h:255
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition: strings.h:143
char * ast_str_set_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring.
Definition: strings.h:1055
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1835
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
ast_app: A registered application
Definition: pbx_app.c:45
Message representing attended transfer.
struct ast_bridge_channel_snapshot_pair to_transfer_target
struct ast_channel_snapshot * transferee
struct ast_bridge_channel_snapshot_pair to_transferee
struct ast_channel_snapshot * target
struct ast_channel_snapshot * channel_snapshot
Structure that contains information regarding a channel in a bridge.
struct ast_bridge * bridge
Bridge this channel is participating in.
unsigned int binaural_suspended
struct ast_channel * chan
Structure that contains features information.
struct ast_flags feature_flags
Structure that contains a snapshot of information about a bridge.
Definition: bridge.h:318
const ast_string_field name
const ast_string_field data
const ast_string_field appl
Structure representing a snapshot of channel state.
struct ast_channel_snapshot_dialplan * dialplan
struct ast_channel_snapshot_base * base
Structure to describe a channel "technology", ie a channel driver See for examples:
Definition: channel.h:648
struct ast_format_cap * capabilities
Definition: channel.h:652
const char *const type
Definition: channel.h:649
const char *const description
Definition: channel.h:650
Main Channel structure associated with a channel.
struct ast_bridge_channel * bridge_channel
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
void * data
Definition: datastore.h:66
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition: lock.h:139
struct ast_party_id id
Caller party ID.
Definition: channel.h:422
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:344
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
Options for ast_pbx_run()
Definition: pbx.h:408
Definition: pbx.h:215
Support for dynamic strings.
Definition: strings.h:623
struct confbridge_conference * conference
struct confbridge_conference * conference
struct ast_channel * initiator
const ast_string_field waitforleader
Definition: confbridge.h:224
const ast_string_field invalidpin
Definition: confbridge.h:224
const ast_string_field placeintoconf
Definition: confbridge.h:224
const ast_string_field onlyone
Definition: confbridge.h:224
const ast_string_field otherinparty
Definition: confbridge.h:224
const ast_string_field unlockednow
Definition: confbridge.h:224
const ast_string_field binauralon
Definition: confbridge.h:224
const ast_string_field getpin
Definition: confbridge.h:224
const ast_string_field errormenu
Definition: confbridge.h:224
const ast_string_field participantsunmuted
Definition: confbridge.h:224
const ast_string_field participantsmuted
Definition: confbridge.h:224
const ast_string_field leave
Definition: confbridge.h:224
const ast_string_field locked
Definition: confbridge.h:224
const ast_string_field join
Definition: confbridge.h:224
const ast_string_field kicked
Definition: confbridge.h:224
const ast_string_field hasleft
Definition: confbridge.h:224
const ast_string_field unmuted
Definition: confbridge.h:224
const ast_string_field leaderhasleft
Definition: confbridge.h:224
const ast_string_field lockednow
Definition: confbridge.h:224
const ast_string_field hasjoin
Definition: confbridge.h:224
const ast_string_field binauraloff
Definition: confbridge.h:224
const ast_string_field onlyperson
Definition: confbridge.h:224
const ast_string_field thereare
Definition: confbridge.h:224
const ast_string_field muted
Definition: confbridge.h:224
const ast_string_field begin
Definition: confbridge.h:224
char rec_options[128]
Definition: confbridge.h:231
unsigned int remb_send_interval
Definition: confbridge.h:241
unsigned int maximum_sample_rate
Definition: confbridge.h:236
unsigned int remb_estimated_bitrate
Definition: confbridge.h:242
char regcontext[AST_MAX_CONTEXT]
Definition: confbridge.h:239
char rec_command[128]
Definition: confbridge.h:232
unsigned int mix_interval
Definition: confbridge.h:237
unsigned int video_update_discard
Definition: confbridge.h:240
unsigned int internal_sample_rate
Definition: confbridge.h:235
char rec_file[PATH_MAX]
Definition: confbridge.h:230
char language[MAX_LANGUAGE]
Definition: confbridge.h:229
unsigned int max_members
Definition: confbridge.h:234
char name[MAX_PROFILE_NAME]
Definition: confbridge.h:228
struct bridge_profile_sounds * sounds
Definition: confbridge.h:238
enum conf_menu_action_id id
Definition: confbridge.h:123
char exten[AST_MAX_EXTENSION]
Definition: confbridge.h:128
struct conf_menu_action::@89 action
struct conf_menu_action::@88::@90 dialplan_args
char playback_file[PATH_MAX]
Definition: confbridge.h:125
char context[AST_MAX_CONTEXT]
Definition: confbridge.h:127
union conf_menu_action::@88 data
Definition: confbridge.h:138
char dtmf[MAXIMUM_DTMF_FEATURE_STRING]
Definition: confbridge.h:140
struct conf_menu_entry::@91 actions
The structure that represents a conference bridge.
Definition: confbridge.h:246
char name[MAX_CONF_NAME]
Definition: confbridge.h:247
struct confbridge_state * state
Definition: confbridge.h:248
unsigned int muted
Definition: confbridge.h:255
struct ast_str * orig_rec_file
Definition: confbridge.h:259
struct ast_str * record_filename
Definition: confbridge.h:258
struct ast_bridge * bridge
Definition: confbridge.h:249
unsigned int activeusers
Definition: confbridge.h:251
struct ast_channel * playback_chan
Definition: confbridge.h:256
unsigned int waitingusers
Definition: confbridge.h:253
struct ast_taskprocessor * playback_queue
Definition: confbridge.h:262
struct bridge_profile b_profile
Definition: confbridge.h:250
unsigned int markedusers
Definition: confbridge.h:252
struct confbridge_conference::@94 active_list
struct ast_channel * record_chan
Definition: confbridge.h:257
struct confbridge_conference::@95 waiting_list
unsigned int locked
Definition: confbridge.h:254
struct confbridge_conference * conference
struct confbridge_user * user
enum ast_bridge_hook_type hook_type
The structure that represents a conference bridge user.
Definition: confbridge.h:273
unsigned int kicked
Definition: confbridge.h:284
unsigned int muted
Definition: confbridge.h:283
struct confbridge_conference * conference
Definition: confbridge.h:274
struct confbridge_user::@98 list
struct ast_channel * chan
Definition: confbridge.h:279
struct user_profile u_profile
Definition: confbridge.h:276
struct confbridge_conference * conference
ast_cond_t cond
ast_mutex_t lock
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:327
int stacklen
Definition: extconf.h:237
struct confbridge_conference * conference
const char * filename
int(* func)(struct confbridge_user *user)
Definition: confbridge.h:268
struct post_join_action::@96 list
structure to hold users read from users.conf
An API for managing task processing threads that can be shared across modules.
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary.
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
@ TPS_REF_DEFAULT
return a reference to a taskprocessor, create one if it does not exist
Definition: taskprocessor.h:76
int ast_taskprocessor_push(struct ast_taskprocessor *tps, int(*task_exe)(void *datap), void *datap) attribute_warn_unused_result
Push a task into the specified taskprocessor queue and signal the taskprocessor thread.
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:61
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
static void handler(const char *name, int response_code, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: test_ari.c:59
const char * args
static struct test_val a
#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
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666