Asterisk - The Open Source Telephony Project GIT-master-545c459
Loading...
Searching...
No Matches
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;
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 (!user && !conference->playback_queue) {
1131 /* Announcements to the entire conference are disabled. Announcements to a
1132 * single caller are played on that caller's channel, so they still work. */
1133 return 0;
1134 }
1135
1136 if (conference->activeusers <= 1) {
1137 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
1138 return 0;
1139 } else if (conference->activeusers == 2) {
1140 if (user) {
1141 /* Eep, there is one other person */
1142 if (play_file(bridge_channel, user->chan, only_one) < 0) {
1143 return -1;
1144 }
1145 } else {
1146 play_sound_file(conference, only_one);
1147 }
1148 } else {
1149 /* Alas multiple others in here */
1150 if (user) {
1151 if (ast_stream_and_wait(user->chan,
1152 there_are,
1153 "")) {
1154 return -1;
1155 }
1156 if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) {
1157 return -1;
1158 }
1159 if (play_file(bridge_channel, user->chan, other_in_party) < 0) {
1160 return -1;
1161 }
1162 } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
1163 play_sound_file(conference, there_are);
1164 play_sound_number(conference, conference->activeusers - 1);
1165 play_sound_file(conference, other_in_party);
1166 }
1167 }
1168 return 0;
1169}
1170
1171/*!
1172 * \brief Play back an audio file to a channel
1173 *
1174 * \param user User to play audio prompt to
1175 * \param filename Prompt to play
1176 *
1177 * \retval 0 on success.
1178 * \retval -1 if the user hung up.
1179 *
1180 * \note Generally this should be called when the conference is unlocked to avoid blocking
1181 * the entire conference while the sound is played. But don't unlock the conference bridge
1182 * in the middle of a state transition.
1183 */
1184static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
1185{
1186 return ast_stream_and_wait(user->chan, filename, "");
1187}
1188
1189static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked)
1190{
1191 /* Right now, only marked users are automatically set as the single src of video.*/
1192 if (!marked) {
1193 return;
1194 }
1195
1197 int set = 1;
1198 struct confbridge_user *user = NULL;
1199
1201 /* see if anyone is already the video src */
1203 if (user->chan == chan) {
1204 continue;
1205 }
1207 set = 0;
1208 break;
1209 }
1210 }
1212 if (set) {
1214 }
1216 /* we joined and are video capable, we override anyone else that may have already been the video feed */
1218 }
1219}
1220
1222{
1223 struct confbridge_user *user = NULL;
1224
1225 /* if this isn't a video source, nothing to update */
1227 return;
1228 }
1229
1231
1232 /* If in follow talker mode, make sure to restore this mode on the
1233 * bridge when a source is removed. It is possible this channel was
1234 * only set temporarily as a video source by an AMI or DTMF action. */
1237 }
1238
1239 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
1242 return;
1243 }
1244
1245 /* Make the next available marked user the video src. */
1248 if (user->chan == chan) {
1249 continue;
1250 }
1251 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1253 break;
1254 }
1255 }
1257}
1258
1266
1267/*!
1268 * \brief Hang up the announcer channel
1269 *
1270 * This hangs up the announcer channel in the conference. This
1271 * runs in the playback queue taskprocessor since we do not want
1272 * to hang up the channel while it's trying to play an announcement.
1273 *
1274 * This task is performed synchronously, so there is no need to
1275 * perform any cleanup on the passed-in data.
1276 *
1277 * \param data A hangup_data structure
1278 * \return 0
1279 */
1280static int hangup_playback(void *data)
1281{
1282 struct hangup_data *hangup = data;
1283
1284 ast_autoservice_stop(hangup->conference->playback_chan);
1285
1286 ast_hangup(hangup->conference->playback_chan);
1287 hangup->conference->playback_chan = NULL;
1288
1289 ast_mutex_lock(&hangup->lock);
1290 hangup->hungup = 1;
1291 ast_cond_signal(&hangup->cond);
1292 ast_mutex_unlock(&hangup->lock);
1293
1294 return 0;
1295}
1296
1298{
1299 ast_mutex_init(&hangup->lock);
1300 ast_cond_init(&hangup->cond, NULL);
1301
1302 hangup->conference = conference;
1303 hangup->hungup = 0;
1304}
1305
1307{
1308 ast_mutex_destroy(&hangup->lock);
1309 ast_cond_destroy(&hangup->cond);
1310}
1311
1312/*!
1313 * \brief Destroy a conference bridge
1314 *
1315 * \param obj The conference bridge object
1316 */
1317static void destroy_conference_bridge(void *obj)
1318{
1319 struct confbridge_conference *conference = obj;
1320
1321 ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
1322
1323 if (conference->playback_chan) {
1324 if (conference->playback_queue) {
1325 struct hangup_data hangup;
1327
1329 ast_mutex_lock(&hangup.lock);
1330 while (!hangup.hungup) {
1331 ast_cond_wait(&hangup.cond, &hangup.lock);
1332 }
1333 ast_mutex_unlock(&hangup.lock);
1334 }
1335
1337 } else {
1338 /* Playback queue is not yet allocated. Just hang up the channel straight */
1341 }
1342 }
1343
1344 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
1345 if (conference->bridge) {
1348 }
1349
1353
1356}
1357
1358/*! \brief Call the proper join event handler for the user for the conference bridge's current state
1359 * \internal
1360 * \param user The conference bridge user that is joining
1361 * \retval 0 success
1362 * \retval -1 failure
1363 */
1365{
1367 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1368 handler = user->conference->state->join_marked;
1369 } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
1370 handler = user->conference->state->join_waitmarked;
1371 } else {
1372 handler = user->conference->state->join_unmarked;
1373 }
1374
1376
1377 if (!handler) {
1379 return -1;
1380 }
1381
1382 handler(user);
1383
1384 return 0;
1385}
1386
1387/*! \brief Call the proper leave event handler for the user for the conference bridge's current state
1388 * \internal
1389 * \param user The conference bridge user that is leaving
1390 * \retval 0 success
1391 * \retval -1 failure
1392 */
1394{
1396 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
1397 handler = user->conference->state->leave_marked;
1398 } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
1399 handler = user->conference->state->leave_waitmarked;
1400 } else {
1401 handler = user->conference->state->leave_unmarked;
1402 }
1403
1405
1406 if (!handler) {
1407 /* This should never happen. If it does, though, it is bad. The user will not have been removed
1408 * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
1409 * Shouldn't happen, though. */
1411 return -1;
1412 }
1413
1414 handler(user);
1415
1416 return 0;
1417}
1418
1420{
1421 int mute_user;
1422 int mute_system;
1423 int mute_effective;
1424
1425 /* User level mute request. */
1426 mute_user = user->muted;
1427
1428 /* System level mute request. */
1429 mute_system = user->playing_moh
1430 /*
1431 * Do not allow waitmarked users to talk to anyone unless there
1432 * is a marked user present.
1433 */
1434 || (!user->conference->markedusers
1435 && ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED));
1436
1437 mute_effective = mute_user || mute_system;
1438
1439 ast_debug(1, "User %s is %s: user:%d system:%d.\n",
1440 ast_channel_name(user->chan), mute_effective ? "muted" : "unmuted",
1441 mute_user, mute_system);
1442 user->features.mute = mute_effective;
1443 ast_test_suite_event_notify("CONF_MUTE_UPDATE",
1444 "Mode: %s\r\n"
1445 "Conference: %s\r\n"
1446 "Channel: %s",
1447 mute_effective ? "muted" : "unmuted",
1448 user->conference->b_profile.name,
1449 ast_channel_name(user->chan));
1450}
1451
1452/*!
1453 * \internal
1454 * \brief Mute/unmute a single user.
1455 */
1457{
1458 /* Set user level mute request. */
1459 user->muted = mute ? 1 : 0;
1460
1462 ast_test_suite_event_notify("CONF_MUTE",
1463 "Message: participant %s %s\r\n"
1464 "Conference: %s\r\n"
1465 "Channel: %s",
1466 ast_channel_name(user->chan),
1467 mute ? "muted" : "unmuted",
1469 ast_channel_name(user->chan));
1470 if (mute) {
1472 } else {
1474 }
1475}
1476
1478{
1479 user->playing_moh = 0;
1480 if (!user->suspended_moh) {
1481 int in_bridge;
1482
1483 /*
1484 * Locking the ast_bridge here is the only way to hold off the
1485 * call to ast_bridge_join() in confbridge_exec() from
1486 * interfering with the bridge and MOH operations here.
1487 */
1488 ast_bridge_lock(user->conference->bridge);
1489
1490 /*
1491 * Temporarily suspend the user from the bridge so we have
1492 * control to stop MOH if needed.
1493 */
1494 in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1495 ast_moh_stop(user->chan);
1496 if (in_bridge) {
1497 ast_bridge_unsuspend(user->conference->bridge, user->chan);
1498 }
1499
1500 ast_bridge_unlock(user->conference->bridge);
1501 }
1502}
1503
1505{
1506 user->playing_moh = 1;
1507 if (!user->suspended_moh) {
1508 int in_bridge;
1509
1510 /*
1511 * Locking the ast_bridge here is the only way to hold off the
1512 * call to ast_bridge_join() in confbridge_exec() from
1513 * interfering with the bridge and MOH operations here.
1514 */
1515 ast_bridge_lock(user->conference->bridge);
1516
1517 /*
1518 * Temporarily suspend the user from the bridge so we have
1519 * control to start MOH if needed.
1520 */
1521 in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1522 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1523 if (in_bridge) {
1524 ast_bridge_unsuspend(user->conference->bridge, user->chan);
1525 }
1526
1527 ast_bridge_unlock(user->conference->bridge);
1528 }
1529}
1530
1531/*!
1532 * \internal
1533 * \brief Unsuspend MOH for the conference user.
1534 *
1535 * \param user Conference user to unsuspend MOH on.
1536 */
1538{
1539 ao2_lock(user->conference);
1540 if (--user->suspended_moh == 0 && user->playing_moh) {
1541 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1542 }
1543 ao2_unlock(user->conference);
1544}
1545
1546/*!
1547 * \internal
1548 * \brief Suspend MOH for the conference user.
1549 *
1550 * \param user Conference user to suspend MOH on.
1551 */
1553{
1554 ao2_lock(user->conference);
1555 if (user->suspended_moh++ == 0 && user->playing_moh) {
1556 ast_moh_stop(user->chan);
1557 }
1558 ao2_unlock(user->conference);
1559}
1560
1562{
1563 /* If we have not been quieted play back that they are waiting for the leader */
1565 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, user->conference->b_profile.sounds))) {
1566 /* user hungup while the sound was playing */
1567 return -1;
1568 }
1569 return 0;
1570}
1571
1573{
1574 /* If audio prompts have not been quieted or this prompt quieted play it on out */
1577 conf_get_sound(CONF_SOUND_ONLY_PERSON, user->conference->b_profile.sounds))) {
1578 /* user hungup while the sound was playing */
1579 return -1;
1580 }
1581 }
1582 return 0;
1583}
1584
1586{
1587 struct post_join_action *action;
1588 if (!(action = ast_calloc(1, sizeof(*action)))) {
1589 return -1;
1590 }
1591 action->func = func;
1592 AST_LIST_INSERT_TAIL(&user->post_join_list, action, list);
1593 return 0;
1594}
1595
1596
1598{
1599 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference->name);
1600}
1601
1603{
1604 /* If we are the second participant we may need to stop music on hold on the first */
1605 struct confbridge_user *first_user = AST_LIST_FIRST(&conference->active_list);
1606
1607 if (ast_test_flag(&first_user->u_profile, USER_OPT_MUSICONHOLD)) {
1608 conf_moh_stop(first_user);
1609 }
1610 conf_update_user_mute(first_user);
1611}
1612
1614{
1615 struct pbx_find_info q = { .stacklen = 0 };
1616
1617 /* Called with a reference to conference */
1618 ao2_unlink(conference_bridges, conference);
1619 send_conf_end_event(conference);
1620 if (!ast_strlen_zero(conference->b_profile.regcontext) &&
1622 conference->name, 1, NULL, "", E_MATCH)) {
1624 conference->name, 1, NULL);
1625 }
1626 ao2_lock(conference);
1627 conf_stop_record(conference);
1628 ao2_unlock(conference);
1629}
1630
1631/*!
1632 * \internal
1633 * \brief Allocate playback channel for a conference.
1634 * \pre expects conference to be locked before calling this function
1635 */
1636static int alloc_playback_chan(struct confbridge_conference *conference)
1637{
1638 struct ast_format_cap *cap;
1639 char taskprocessor_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1640
1642 if (!cap) {
1643 return -1;
1644 }
1646 conference->playback_chan = ast_request("CBAnn", cap, NULL, NULL,
1647 conference->name, NULL);
1648 ao2_ref(cap, -1);
1649 if (!conference->playback_chan) {
1650 return -1;
1651 }
1652
1653 /* To make sure playback_chan has the same language as the bridge */
1654 ast_channel_lock(conference->playback_chan);
1655 ast_channel_language_set(conference->playback_chan, conference->b_profile.language);
1656 ast_channel_unlock(conference->playback_chan);
1657
1658 ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
1659 ast_channel_name(conference->playback_chan), conference->name);
1660
1661 ast_taskprocessor_build_name(taskprocessor_name, sizeof(taskprocessor_name),
1662 "Confbridge/%s", conference->name);
1663 conference->playback_queue = ast_taskprocessor_get(taskprocessor_name, TPS_REF_DEFAULT);
1664 if (!conference->playback_queue) {
1665 ast_hangup(conference->playback_chan);
1666 conference->playback_chan = NULL;
1667 return -1;
1668 }
1669 return 0;
1670}
1671
1672/*!
1673 * \brief Push the announcer channel into the bridge
1674 *
1675 * \param conference Conference bridge to push the announcer to
1676 * \retval 0 Success
1677 * \retval -1 Failed to push the channel to the bridge
1678 */
1679static int push_announcer(struct confbridge_conference *conference)
1680{
1681 if (conf_announce_channel_push(conference->playback_chan)) {
1682 ast_hangup(conference->playback_chan);
1683 conference->playback_chan = NULL;
1684 return -1;
1685 }
1686
1688 return 0;
1689}
1690
1691static void confbridge_unlock_and_unref(void *obj)
1692{
1693 struct confbridge_conference *conference = obj;
1694
1695 if (!obj) {
1696 return;
1697 }
1698 ao2_unlock(conference);
1699 ao2_ref(conference, -1);
1700}
1701
1703{
1704 struct ast_channel_snapshot *old_snapshot;
1705 struct ast_channel_snapshot *new_snapshot;
1706 char *confbr_name = NULL;
1707 char *comma;
1709 struct confbridge_user *user = NULL;
1710 int found_user = 0;
1711 struct ast_json *json_object;
1712
1714 && strcmp(msg->to_transferee.channel_snapshot->dialplan->appl, "ConfBridge") == 0
1715 && msg->target) {
1716 /* We're transferring a bridge to an extension */
1717 old_snapshot = msg->to_transferee.channel_snapshot;
1718 new_snapshot = msg->target;
1719 } else if (msg->to_transfer_target.channel_snapshot
1720 && strcmp(msg->to_transfer_target.channel_snapshot->dialplan->appl, "ConfBridge") == 0
1721 && msg->transferee) {
1722 /* We're transferring a call to a bridge */
1723 old_snapshot = msg->to_transfer_target.channel_snapshot;
1724 new_snapshot = msg->transferee;
1725 } else {
1726 ast_log(LOG_ERROR, "Could not determine proper channels\n");
1727 return;
1728 }
1729
1730 /*
1731 * old_snapshot->data should have the original parameters passed to
1732 * the ConfBridge app:
1733 * conference[,bridge_profile[,user_profile[,menu]]]
1734 * We'll use "conference" to look up the bridge.
1735 *
1736 * We _could_ use old_snapshot->bridgeid to get the bridge but
1737 * that would involve locking the conference_bridges container
1738 * and iterating over it looking for a matching bridge.
1739 */
1740 if (ast_strlen_zero(old_snapshot->dialplan->data)) {
1741 ast_log(LOG_ERROR, "Channel '%s' didn't have app data set\n", old_snapshot->base->name);
1742 return;
1743 }
1744 confbr_name = ast_strdupa(old_snapshot->dialplan->data);
1745 comma = strchr(confbr_name, ',');
1746 if (comma) {
1747 *comma = '\0';
1748 }
1749
1750 ast_debug(1, "Confbr: %s Leaving: %s Joining: %s\n", confbr_name, old_snapshot->base->name, new_snapshot->base->name);
1751
1752 conference = ao2_find(conference_bridges, confbr_name, OBJ_SEARCH_KEY);
1753 if (!conference) {
1754 ast_log(LOG_ERROR, "Conference bridge '%s' not found\n", confbr_name);
1755 return;
1756 }
1757 ao2_lock(conference);
1758
1759 /*
1760 * We need to grab the user profile for the departing user in order to
1761 * properly format the join/leave messages.
1762 */
1763 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
1764 if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
1765 found_user = 1;
1766 break;
1767 }
1768 }
1769
1770 /*
1771 * If we didn't find the user in the active list, try the waiting list.
1772 */
1773 if (!found_user && conference->waitingusers) {
1774 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
1775 if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
1776 found_user = 1;
1777 break;
1778 }
1779 }
1780 }
1781
1782 if (!found_user) {
1783 ast_log(LOG_ERROR, "Unable to find user profile for channel '%s' in bridge '%s'\n",
1784 old_snapshot->base->name, confbr_name);
1785 return;
1786 }
1787
1788 /*
1789 * We're going to use the existing user profile to create the messages.
1790 */
1791 json_object = ast_json_pack("{s: b}",
1792 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
1793 );
1794 if (!json_object) {
1795 return;
1796 }
1797
1798 send_conf_stasis_snapshots(conference, old_snapshot, confbridge_leave_type(), json_object);
1799 ast_json_unref(json_object);
1800
1801 json_object = ast_json_pack("{s: b, s: b}",
1802 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN),
1803 "muted", user->muted);
1804 if (!json_object) {
1805 return;
1806 }
1807 send_conf_stasis_snapshots(conference, new_snapshot, confbridge_join_type(), json_object);
1808 ast_json_unref(json_object);
1809}
1810
1811/*!
1812 * \brief Join a conference bridge
1813 *
1814 * \param conference_name The conference name
1815 * \param user Conference bridge user structure
1816 *
1817 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1818 */
1819static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user)
1820{
1821 struct confbridge_conference *conference;
1822 struct post_join_action *action;
1823 int max_members_reached = 0;
1824
1825 /* We explicitly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same time */
1827
1828 ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name);
1829
1830 /* Attempt to find an existing conference bridge */
1831 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
1832 if (conference && conference->b_profile.max_members) {
1833 max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1;
1834 }
1835
1836 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1837 if (conference && (max_members_reached || conference->locked) && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
1839 ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", conference_name);
1842 "");
1843 ao2_ref(conference, -1);
1844 return NULL;
1845 }
1846
1847 /* If no conference bridge was found see if we can create one */
1848 if (!conference) {
1849 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1850 if (!(conference = ao2_alloc(sizeof(*conference), destroy_conference_bridge))) {
1852 ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", conference_name);
1853 return NULL;
1854 }
1855
1856 /* Setup for the record channel */
1858 if (!conference->record_filename) {
1859 ao2_ref(conference, -1);
1861 return NULL;
1862 }
1863
1864 /* Setup conference bridge parameters */
1865 ast_copy_string(conference->name, conference_name, sizeof(conference->name));
1866 conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
1867
1868 /* Create an actual bridge that will do the audio mixing */
1871 app, conference_name, NULL);
1872 if (!conference->bridge) {
1873 ao2_ref(conference, -1);
1875 ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name);
1876 return NULL;
1877 }
1878
1879 /* Set the internal sample rate on the bridge from the bridge profile */
1881 /* Set the maximum sample rate on the bridge from the bridge profile */
1883 /* Set the internal mixing interval on the bridge from the bridge profile */
1886
1889 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
1894 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST)) {
1896 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
1904 } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_FORCE)) {
1907 }
1908 }
1909
1910 /* Always set the minimum interval between video updates, to avoid infinite video updates. */
1912
1914 ast_bridge_set_send_sdp_label(conference->bridge, 1);
1915 }
1916
1917 /* Link it into the conference bridges container */
1918 if (!ao2_link(conference_bridges, conference)) {
1919 ao2_ref(conference, -1);
1922 "Conference '%s' could not be added to the conferences list.\n", conference_name);
1923 return NULL;
1924 }
1925
1926 /* Set the initial state to EMPTY */
1927 conference->state = CONF_STATE_EMPTY;
1928
1929 /* The announcer channel lives for as long as the conference does, so whether it
1930 * gets created is decided once here, using the profile of the channel that
1931 * creates the conference. Leaving playback_chan and playback_queue NULL is what
1932 * tells the rest of this module that announcements are not available. */
1934 if (alloc_playback_chan(conference)) {
1935 ao2_unlink(conference_bridges, conference);
1936 ao2_ref(conference, -1);
1938 ast_log(LOG_ERROR, "Could not allocate announcer channel for conference '%s'\n", conference_name);
1939 return NULL;
1940 }
1941
1942 if (push_announcer(conference)) {
1943 ao2_unlink(conference_bridges, conference);
1944 ao2_ref(conference, -1);
1946 ast_log(LOG_ERROR, "Could not add announcer channel for conference '%s' bridge\n", conference_name);
1947 return NULL;
1948 }
1949 } else {
1950 ast_debug(1, "Announcements are disabled for conference '%s', not creating an announcer channel\n",
1951 conference_name);
1952 }
1953
1955 ao2_lock(conference);
1956 conf_start_record(conference);
1957 ao2_unlock(conference);
1958 }
1959
1960 send_conf_start_event(conference);
1961
1962 if (!ast_strlen_zero(conference->b_profile.regcontext)) {
1963 if (!ast_exists_extension(NULL, conference->b_profile.regcontext, conference->name, 1, NULL)) {
1964 ast_add_extension(conference->b_profile.regcontext, 1, conference->name, 1, NULL, NULL, "Noop", NULL, NULL, "ConfBridge");
1965 }
1966 }
1967
1968 ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);
1969 }
1970
1972
1973 /* Setup conference bridge user parameters */
1974 user->conference = conference;
1975
1976 ao2_lock(conference);
1977
1978 /* Determine if the new user should join the conference muted. */
1979 if (ast_test_flag(&user->u_profile, USER_OPT_STARTMUTED)
1980 || (!ast_test_flag(&user->u_profile, USER_OPT_ADMIN) && conference->muted)) {
1981 /* Set user level mute request. */
1982 user->muted = 1;
1983 }
1984
1985 /*
1986 * Suspend any MOH until the user actually joins the bridge of
1987 * the conference. This way any pre-join file playback does not
1988 * need to worry about MOH.
1989 */
1990 user->suspended_moh = 1;
1991
1993 /* Invalid event, nothing was done, so we don't want to process a leave. */
1994 ao2_unlock(conference);
1995 ao2_ref(conference, -1);
1996 user->conference = NULL;
1997 return NULL;
1998 }
1999
2000 if (ast_check_hangup(user->chan)) {
2001 ao2_unlock(conference);
2003 return NULL;
2004 }
2005
2006 ao2_unlock(conference);
2007
2008 /* If an announcement is to be played play it */
2009 if (!ast_strlen_zero(user->u_profile.announcement)) {
2011 user->u_profile.announcement)) {
2013 return NULL;
2014 }
2015 }
2016
2017 /* Announce number of users if need be */
2018 if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
2019 if (announce_user_count(conference, user, NULL)) {
2021 return NULL;
2022 }
2023 }
2024
2026 (conference->activeusers > user->u_profile.announce_user_count_all_after)) {
2027 int user_count_res;
2028
2029 /*
2030 * We have to autoservice the new user because he has not quite
2031 * joined the conference yet.
2032 */
2034 user_count_res = announce_user_count(conference, NULL, NULL);
2036 if (user_count_res) {
2038 return NULL;
2039 }
2040 }
2041
2042 /* Handle post-join actions */
2043 while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
2044 action->func(user);
2045 ast_free(action);
2046 }
2047
2048 return conference;
2049}
2050
2051/*!
2052 * \brief Leave a conference
2053 *
2054 * \param user The conference user
2055 */
2057{
2058 struct post_join_action *action;
2059
2060 ao2_lock(user->conference);
2062 ao2_unlock(user->conference);
2063
2064 /* Discard any post-join actions */
2065 while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
2066 ast_free(action);
2067 }
2068
2069 /* Done mucking with the conference, huzzah */
2070 ao2_ref(user->conference, -1);
2071 user->conference = NULL;
2072}
2073
2074static void playback_common(struct confbridge_conference *conference, const char *filename, int say_number)
2075{
2076 /* Don't try to play if the playback channel has been hung up */
2077 if (!conference->playback_chan) {
2078 return;
2079 }
2080
2082
2083 /* The channel is all under our control, in goes the prompt */
2084 if (!ast_strlen_zero(filename)) {
2085 ast_stream_and_wait(conference->playback_chan, filename, "");
2086 } else if (say_number >= 0) {
2087 ast_say_number(conference->playback_chan, say_number, "",
2089 }
2090
2092}
2093
2102
2103/*!
2104 * \brief Play an announcement into a confbridge
2105 *
2106 * This runs in the playback queue taskprocessor. This ensures that
2107 * all playbacks are handled in sequence and do not play over top one
2108 * another.
2109 *
2110 * This task runs synchronously so there is no need for performing any
2111 * sort of cleanup on the input parameter.
2112 *
2113 * \param data A playback_task_data
2114 * \return 0
2115 */
2116static int playback_task(void *data)
2117{
2118 struct playback_task_data *ptd = data;
2119
2121
2122 ast_mutex_lock(&ptd->lock);
2123 ptd->playback_finished = 1;
2124 ast_cond_signal(&ptd->cond);
2125 ast_mutex_unlock(&ptd->lock);
2126
2127 return 0;
2128}
2129
2131 const char *filename, int say_number)
2132{
2133 ast_mutex_init(&ptd->lock);
2134 ast_cond_init(&ptd->cond, NULL);
2135
2136 ptd->filename = filename;
2137 ptd->say_number = say_number;
2138 ptd->conference = conference;
2139 ptd->playback_finished = 0;
2140}
2141
2143{
2144 ast_mutex_destroy(&ptd->lock);
2145 ast_cond_destroy(&ptd->cond);
2146}
2147
2149{
2150 struct playback_task_data ptd;
2151
2152 /* Announcements are disabled for this conference, there is nothing to play them on */
2153 if (!conference->playback_queue) {
2154 return 0;
2155 }
2156
2157 /* Do not waste resources trying to play files that do not exist */
2159 if (say_number < 0) {
2160 return 0;
2161 }
2162 } else if (!sound_file_exists(filename)) {
2163 return 0;
2164 }
2165
2168 if (!ast_strlen_zero(filename)) {
2169 ast_log(LOG_WARNING, "Unable to play file '%s' to conference %s\n",
2171 } else {
2172 ast_log(LOG_WARNING, "Unable to say number '%d' to conference %s\n",
2174 }
2176 return -1;
2177 }
2178
2179 /* Wait for the playback to complete */
2180 ast_mutex_lock(&ptd.lock);
2181 while (!ptd.playback_finished) {
2182 ast_cond_wait(&ptd.cond, &ptd.lock);
2183 }
2184 ast_mutex_unlock(&ptd.lock);
2185
2187
2188 return 0;
2189}
2190
2192{
2194}
2195
2202
2208
2209static void async_datastore_data_destroy(void *data)
2210{
2211 struct async_datastore_data *add = data;
2212
2213 ast_mutex_destroy(&add->lock);
2214 ast_cond_destroy(&add->cond);
2215
2216 ast_free(add);
2217}
2218
2219/*!
2220 * \brief Datastore used for timing of async announcement playback
2221 *
2222 * Announcements that are played to the entire conference can be played
2223 * asynchronously (i.e. The channel that queues the playback does not wait
2224 * for the playback to complete before continuing)
2225 *
2226 * The thing about async announcements is that the channel that queues the
2227 * announcement is either not in the bridge or is in some other way "occupied"
2228 * at the time the announcement is queued. Because of that, the initiator of
2229 * the announcement may enter after the announcement has already started,
2230 * resulting in the sound being "clipped".
2231 *
2232 * This datastore makes it so that the channel that queues the async announcement
2233 * can say "I'm ready now". This way the announcement does not start until the
2234 * initiator of the announcement is ready to hear the sound.
2235 */
2237 .type = "Confbridge async playback",
2239};
2240
2242{
2243 struct async_datastore_data *add;
2244
2245 add = ast_malloc(sizeof(*add));
2246 if (!add) {
2247 return NULL;
2248 }
2249
2250 ast_mutex_init(&add->lock);
2251 ast_cond_init(&add->cond, NULL);
2252 add->wait = 1;
2253
2254 return add;
2255}
2256
2257/*!
2258 * \brief Prepare the async playback datastore
2259 *
2260 * This is done prior to queuing an async announcement. If the
2261 * datastore has not yet been created, it is allocated and initialized.
2262 * If it already exists, we set it to be in "waiting" mode.
2263 *
2264 * \param initiator The channel that is queuing the async playback
2265 * \retval 0 Success
2266 * \retval -1 Failure :(
2267 */
2268static int setup_async_playback_datastore(struct ast_channel *initiator)
2269{
2270 struct ast_datastore *async_datastore;
2271
2272 async_datastore = ast_channel_datastore_find(initiator, &async_datastore_info, NULL);
2273 if (async_datastore) {
2274 struct async_datastore_data *add;
2275
2276 add = async_datastore->data;
2277 add->wait = 1;
2278
2279 return 0;
2280 }
2281
2282 async_datastore = ast_datastore_alloc(&async_datastore_info, NULL);
2283 if (!async_datastore) {
2284 return -1;
2285 }
2286
2287 async_datastore->data = async_datastore_data_alloc();
2288 if (!async_datastore->data) {
2289 ast_datastore_free(async_datastore);
2290 return -1;
2291 }
2292
2293 ast_channel_datastore_add(initiator, async_datastore);
2294 return 0;
2295}
2296
2298 struct confbridge_conference *conference, const char *filename, int say_number,
2299 struct ast_channel *initiator)
2300{
2301 struct async_playback_task_data *aptd;
2302
2303 aptd = ast_malloc(sizeof(*aptd) + strlen(filename) + 1);
2304 if (!aptd) {
2305 return NULL;
2306 }
2307
2308 /* Safe */
2309 strcpy(aptd->filename, filename);
2310 aptd->say_number = say_number;
2311
2312 /* You may think that we need to bump the conference refcount since we are pushing
2313 * this task to the taskprocessor.
2314 *
2315 * In this case, that actually causes a problem. The destructor for the conference
2316 * pushes a hangup task into the taskprocessor and waits for it to complete before
2317 * continuing. If the destructor gets called from a taskprocessor task, we're
2318 * deadlocked.
2319 *
2320 * So is there a risk of the conference being freed out from under us? No. Since
2321 * the destructor pushes a task into the taskprocessor and waits for it to complete,
2322 * the destructor cannot free the conference out from under us. No further tasks
2323 * can be queued onto the taskprocessor after the hangup since no channels are referencing
2324 * the conference at that point any more.
2325 */
2326 aptd->conference = conference;
2327
2328 aptd->initiator = initiator;
2329 if (initiator) {
2332 /* We don't really care if this fails. If the datastore fails to get set up
2333 * we'll still play the announcement. It's possible that the sound will be
2334 * clipped for the initiator, but that's not the end of the world.
2335 */
2338 }
2339
2340 return aptd;
2341}
2342
2344{
2346 ast_free(aptd);
2347}
2348
2349/*!
2350 * \brief Wait for the initiator of an async playback to be ready
2351 *
2352 * See the description on the async_datastore_info structure for more
2353 * information about what this is about.
2354 *
2355 * \param initiator The channel that queued the async announcement
2356 */
2358{
2359 struct ast_datastore *async_datastore;
2360 struct async_datastore_data *add;
2361
2362 ast_channel_lock(initiator);
2363 async_datastore = ast_channel_datastore_find(initiator, &async_datastore_info, NULL);
2364 ast_channel_unlock(initiator);
2365
2366 if (!async_datastore) {
2367 return;
2368 }
2369
2370 add = async_datastore->data;
2371
2372 ast_mutex_lock(&add->lock);
2373 while (add->wait) {
2374 ast_cond_wait(&add->cond, &add->lock);
2375 }
2376 ast_mutex_unlock(&add->lock);
2377}
2378
2379/*!
2380 * \brief Play an announcement into a confbridge asynchronously
2381 *
2382 * This runs in the playback queue taskprocessor. This ensures that
2383 * all playbacks are handled in sequence and do not play over top one
2384 * another.
2385 *
2386 * \param data An async_playback_task_data
2387 * \return 0
2388 */
2389static int async_playback_task(void *data)
2390{
2391 struct async_playback_task_data *aptd = data;
2392
2393 /* Wait for the initiator to get back in the bridge or be hung up */
2394 if (aptd->initiator) {
2396 }
2397
2398 playback_common(aptd->conference, aptd->filename, aptd->say_number);
2399
2401 return 0;
2402}
2403
2405 const char *filename, int say_number, struct ast_channel *initiator)
2406{
2407 struct async_playback_task_data *aptd;
2408
2409 /* Announcements are disabled for this conference, there is nothing to play them on */
2410 if (!conference->playback_queue) {
2411 return 0;
2412 }
2413
2414 /* Do not waste resources trying to play files that do not exist */
2416 if (say_number < 0) {
2417 return 0;
2418 }
2419 } else if (!sound_file_exists(filename)) {
2420 return 0;
2421 }
2422
2424 if (!aptd) {
2425 return -1;
2426 }
2427
2429 if (!ast_strlen_zero(filename)) {
2430 ast_log(LOG_WARNING, "Unable to play file '%s' to conference '%s'\n",
2432 } else {
2433 ast_log(LOG_WARNING, "Unable to say number '%d' to conference '%s'\n",
2435 }
2437 return -1;
2438 }
2439
2440 return 0;
2441}
2442
2448
2450{
2451 struct ast_datastore *async_datastore;
2452 struct async_datastore_data *add;
2453
2454 ast_channel_lock(chan);
2455 async_datastore = ast_channel_datastore_find(chan, &async_datastore_info, NULL);
2456 ast_channel_unlock(chan);
2457 if (!async_datastore) {
2458 return;
2459 }
2460
2461 add = async_datastore->data;
2462
2463 ast_mutex_lock(&add->lock);
2464 add->wait = 0;
2465 ast_cond_signal(&add->cond);
2466 ast_mutex_unlock(&add->lock);
2467}
2468
2469/*!
2470 * \brief Play number into the conference bridge
2471 *
2472 * \param conference The conference bridge to say the number into
2473 * \param say_number number to say
2474 *
2475 * \retval 0 success
2476 * \retval -1 failure
2477 */
2478static int play_sound_number(struct confbridge_conference *conference, int say_number)
2479{
2480 return play_sound_helper(conference, NULL, say_number);
2481}
2482
2483static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking)
2484{
2485 struct confbridge_user *user = hook_pvt;
2487 struct ast_json *talking_extras;
2488
2489 conference = ao2_find(conference_bridges, user->conference->name, OBJ_KEY);
2490 if (!conference) {
2491 /* Remove the hook since the conference does not exist. */
2492 return -1;
2493 }
2494
2495 ao2_lock(conference);
2496 user->talking = talking;
2497 ao2_unlock(conference);
2498
2499 talking_extras = ast_json_pack("{s: s, s: b}",
2500 "talking_status", talking ? "on" : "off",
2501 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN));
2502 if (!talking_extras) {
2503 return 0;
2504 }
2505
2506 send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0);
2507 ast_json_unref(talking_extras);
2508 return 0;
2509}
2510
2511static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
2512{
2513 char pin_guess[MAX_PIN+1] = { 0, };
2514 const char *pin = user->u_profile.pin;
2515 char *tmp = pin_guess;
2516 int i, res;
2517 unsigned int len = MAX_PIN;
2518
2519 /*
2520 * NOTE: We have not joined a conference yet so we have to use
2521 * the bridge profile requested by the user.
2522 */
2523
2524 /* give them three tries to get the pin right */
2525 for (i = 0; i < 3; i++) {
2526 if (ast_app_getdata(chan,
2527 conf_get_sound(CONF_SOUND_GET_PIN, user->b_profile.sounds),
2528 tmp, len, 0) >= 0) {
2529 if (!strcasecmp(pin, pin_guess)) {
2530 return 0;
2531 }
2532 }
2533 ast_streamfile(chan,
2534 conf_get_sound(CONF_SOUND_INVALID_PIN, user->b_profile.sounds),
2535 ast_channel_language(chan));
2536 res = ast_waitstream(chan, AST_DIGIT_ANY);
2537 if (res > 0) {
2538 /* Account for digit already read during ivalid pin playback
2539 * resetting pin buf. */
2540 pin_guess[0] = res;
2541 pin_guess[1] = '\0';
2542 tmp = pin_guess + 1;
2543 len = MAX_PIN - 1;
2544 } else {
2545 /* reset pin buf as empty buffer. */
2546 tmp = pin_guess;
2547 len = MAX_PIN;
2548 }
2549 }
2550 return -1;
2551}
2552
2553static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
2554{
2556 pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "TIMEOUT");
2557 return -1;
2558}
2559
2560static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
2561{
2562 char destdir[PATH_MAX];
2563 int res;
2564 int duration = 20;
2565
2566 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
2567
2568 if (ast_mkdir(destdir, 0777) != 0) {
2569 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2570 return -1;
2571 }
2572 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
2573 "%s/confbridge-name-%s-%s", destdir,
2574 conf_name, ast_channel_uniqueid(user->chan));
2575
2577 res = ast_play_and_record(user->chan,
2578 "vm-rec-name",
2579 user->name_rec_location,
2580 10,
2581 "sln",
2582 &duration,
2583 NULL,
2585 0,
2586 NULL);
2587 } else {
2588 res = ast_record_review(user->chan,
2589 "vm-rec-name",
2590 user->name_rec_location,
2591 10,
2592 "sln",
2593 &duration,
2594 NULL);
2595 }
2596
2597 if (res == -1) {
2598 ast_filedelete(user->name_rec_location, NULL);
2599 user->name_rec_location[0] = '\0';
2600 return -1;
2601 }
2602 return 0;
2603}
2604
2609
2611 struct confbridge_conference *conference, const char *filename)
2612{
2614
2615 atd = ast_malloc(sizeof(*atd) + strlen(filename) + 1);
2616 if (!atd) {
2617 return NULL;
2618 }
2619
2620 /* Safe */
2621 strcpy(atd->filename, filename);
2622 atd->conference = conference;
2623
2624 return atd;
2625}
2626
2631
2632/*!
2633 * \brief Delete user's name file asynchronously
2634 *
2635 * This runs in the playback queue taskprocessor. This ensures that
2636 * sound file is removed after playback is finished and not before.
2637 *
2638 * \param data An async_delete_name_rec_task_data
2639 * \return 0
2640 */
2641static int async_delete_name_rec_task(void *data)
2642{
2643 struct async_delete_name_rec_task_data *atd = data;
2644
2646 ast_log(LOG_DEBUG, "Conference '%s' removed user name file '%s'\n",
2647 atd->conference->name, atd->filename);
2648
2650 return 0;
2651}
2652
2654 const char *filename)
2655{
2657
2659 return 0;
2660 } else if (!sound_file_exists(filename)) {
2661 return 0;
2662 }
2663
2664 /* Without a playback queue there is no playback to wait for, so just remove it now */
2665 if (!conference->playback_queue) {
2667 ast_debug(1, "Conference '%s' removed user name file '%s'\n",
2669 return 0;
2670 }
2671
2673 if (!atd) {
2674 return -1;
2675 }
2676
2678 ast_log(LOG_WARNING, "Conference '%s' was unable to remove user name file '%s'\n",
2681 return -1;
2682 }
2683
2684 return 0;
2685}
2686
2687static int join_callback(struct ast_bridge_channel *bridge_channel, void *ignore)
2688{
2689 async_play_sound_ready(bridge_channel->chan);
2690 return 0;
2691}
2692
2698
2699static int send_event_hook_callback(struct ast_bridge_channel *bridge_channel, void *data)
2700{
2701 struct confbridge_hook_data *hook_data = data;
2702
2703 if (hook_data->hook_type == AST_BRIDGE_HOOK_TYPE_JOIN) {
2704 send_join_event(hook_data->user, hook_data->conference);
2705 } else {
2706 send_leave_event(hook_data->user, hook_data->conference);
2707 }
2708
2709 return 0;
2710}
2711
2712/*! \brief The ConfBridge application */
2713static int confbridge_exec(struct ast_channel *chan, const char *data)
2714{
2715 int res = 0, volume_adjustments[2];
2716 int quiet = 0;
2717 int async_delete_task_pushed = 0;
2718 char *parse;
2719 const char *b_profile_name = NULL;
2720 const char *u_profile_name = NULL;
2721 const char *menu_profile_name = NULL;
2722 struct confbridge_conference *conference = NULL;
2723 struct confbridge_user user = {
2724 .chan = chan,
2725 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
2726 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
2727 .tech_args.drop_silence = 0,
2728 };
2729 struct confbridge_hook_data *join_hook_data;
2730 struct confbridge_hook_data *leave_hook_data;
2731
2733 AST_APP_ARG(conf_name);
2734 AST_APP_ARG(b_profile_name);
2735 AST_APP_ARG(u_profile_name);
2736 AST_APP_ARG(menu_profile_name);
2737 );
2738
2739 if (ast_bridge_features_init(&user.features)) {
2740 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2741 res = -1;
2742 goto confbridge_cleanup;
2743 }
2744
2745 /* We need to make a copy of the input string if we are going to modify it! */
2746 parse = ast_strdupa(data);
2747
2749
2750 if (ast_strlen_zero(args.conf_name)) {
2751 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2752 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
2753 res = -1;
2754 goto confbridge_cleanup;
2755 }
2756
2757 if (strlen(args.conf_name) >= MAX_CONF_NAME) {
2758 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2759 ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
2760 res = -1;
2761 goto confbridge_cleanup;
2762 }
2763
2764 /* bridge profile name */
2765 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
2766 b_profile_name = args.b_profile_name;
2767 }
2768 if (!conf_find_bridge_profile(chan, b_profile_name, &user.b_profile)) {
2769 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2770 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name ?
2771 b_profile_name : DEFAULT_BRIDGE_PROFILE);
2772 res = -1;
2773 goto confbridge_cleanup;
2774 }
2775
2776 /* user profile name */
2777 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
2778 u_profile_name = args.u_profile_name;
2779 }
2780 if (!conf_find_user_profile(chan, u_profile_name, &user.u_profile)) {
2781 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2782 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name ?
2783 u_profile_name : DEFAULT_USER_PROFILE);
2784 res = -1;
2785 goto confbridge_cleanup;
2786 }
2787
2788 /* If channel hasn't been answered already, answer it, unless we're explicitly not supposed to */
2789 if ((ast_channel_state(chan) != AST_STATE_UP) && (ast_test_flag(&user.u_profile, USER_OPT_ANSWER_CHANNEL))) {
2790 ast_answer(chan);
2791 }
2792
2793 quiet = ast_test_flag(&user.u_profile, USER_OPT_QUIET);
2794
2795 /* ask for a PIN immediately after finding user profile. This has to be
2796 * prompted for requardless of quiet setting. */
2797 if (!ast_strlen_zero(user.u_profile.pin)) {
2798 if (conf_get_pin(chan, &user)) {
2799 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2800 res = -1; /* invalid PIN */
2801 goto confbridge_cleanup;
2802 }
2803 }
2804
2805 /* See if we need them to record a intro name. There is no point in asking for one
2806 * when announcements are disabled, since it could never be played. The conference
2807 * does not necessarily exist yet, so this caller's own bridge profile is used. */
2808 if (!quiet && ast_test_flag(&user.b_profile, BRIDGE_OPT_ANNOUNCEMENTS) &&
2811 if (conf_rec_name(&user, args.conf_name)) {
2812 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2813 res = -1; /* Hangup during name recording */
2814 goto confbridge_cleanup;
2815 }
2816 }
2817
2818 /* menu name */
2819 if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) {
2820 menu_profile_name = args.menu_profile_name;
2821 }
2822
2823 if (conf_set_menu_to_user(chan, &user, menu_profile_name)) {
2824 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2825 ast_log(LOG_WARNING, "Conference menu profile %s does not exist\n", menu_profile_name ?
2826 menu_profile_name : DEFAULT_MENU_PROFILE);
2827 res = -1;
2828 goto confbridge_cleanup;
2829 }
2830
2831 /* Set if DTMF should pass through for this user or not */
2832 if (ast_test_flag(&user.u_profile, USER_OPT_DTMF_PASS)) {
2833 user.features.dtmf_passthrough = 1;
2834 } else {
2835 user.features.dtmf_passthrough = 0;
2836 }
2837
2838 /* Set if text messaging is enabled for this user or not */
2839 if (ast_test_flag(&user.u_profile, USER_OPT_TEXT_MESSAGING)) {
2840 user.features.text_messaging = 1;
2841 } else {
2842 user.features.text_messaging = 0;
2843 }
2844
2845 /* Set dsp threshold values if present */
2846 if (user.u_profile.talking_threshold) {
2847 user.tech_args.talking_threshold = user.u_profile.talking_threshold;
2848 }
2849 if (user.u_profile.silence_threshold) {
2850 user.tech_args.silence_threshold = user.u_profile.silence_threshold;
2851 }
2852
2853 /* Set a talker indicate call back if talking detection is requested */
2854 if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) {
2857 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2858 res = -1;
2859 goto confbridge_cleanup;
2860 }
2861 }
2862
2863 /* Look for a conference bridge matching the provided name */
2864 if (!(conference = join_conference_bridge(args.conf_name, &user))) {
2865 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
2866 res = -1;
2867 goto confbridge_cleanup;
2868 }
2869
2870 /* Keep a copy of volume adjustments so we can restore them later if need be */
2871 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
2872 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
2873
2874 if (ast_test_flag(&user.u_profile, USER_OPT_DROP_SILENCE)) {
2875 user.tech_args.drop_silence = 1;
2876 }
2877
2878 if (ast_test_flag(&user.u_profile, USER_OPT_JITTERBUFFER)) {
2879 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
2880 }
2881
2882 if (ast_test_flag(&user.u_profile, USER_OPT_DENOISE)) {
2883 ast_func_write(chan, "DENOISE(rx)", "on");
2884 }
2885
2886 /* if this user has a intro, play it before entering */
2887 if (!ast_strlen_zero(user.name_rec_location)) {
2889 play_sound_file(conference, user.name_rec_location);
2893 }
2894
2895 if (!quiet) {
2896 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference->b_profile.sounds);
2897
2898 /* if hear_own_join_sound is enabled play the Join sound to everyone */
2900 if (strcmp(conference->b_profile.language, ast_channel_language(chan))) {
2901 ast_stream_and_wait(chan, join_sound, "");
2903 play_sound_file(conference, join_sound);
2905 } else {
2906 async_play_sound_file(conference, join_sound, chan);
2907 }
2908 /* if hear_own_join_sound is disabled only play the Join sound to just the conference */
2909 } else {
2911 play_sound_file(conference, join_sound);
2913 }
2914 }
2915
2916 if (user.u_profile.timeout) {
2918 0,
2919 user.u_profile.timeout * 1000,
2921 NULL,
2922 NULL,
2924 }
2925
2926 /* See if we need to automatically set this user as a video source or not */
2928
2930
2931 join_hook_data = ast_malloc(sizeof(*join_hook_data));
2932 if (!join_hook_data) {
2933 res = -1;
2934 goto confbridge_cleanup;
2935 }
2936 join_hook_data->user = &user;
2937 join_hook_data->conference = conference;
2938 join_hook_data->hook_type = AST_BRIDGE_HOOK_TYPE_JOIN;
2940 join_hook_data, ast_free_ptr, 0);
2941 if (res) {
2942 ast_free(join_hook_data);
2943 ast_log(LOG_ERROR, "Couldn't add bridge join hook for channel '%s'\n", ast_channel_name(chan));
2944 goto confbridge_cleanup;
2945 }
2946
2947 leave_hook_data = ast_malloc(sizeof(*leave_hook_data));
2948 if (!leave_hook_data) {
2949 /* join_hook_data is cleaned up by ast_bridge_features_cleanup via the goto */
2950 res = -1;
2951 goto confbridge_cleanup;
2952 }
2953 leave_hook_data->user = &user;
2954 leave_hook_data->conference = conference;
2955 leave_hook_data->hook_type = AST_BRIDGE_HOOK_TYPE_LEAVE;
2957 leave_hook_data, ast_free_ptr, 0);
2958 if (res) {
2959 /* join_hook_data is cleaned up by ast_bridge_features_cleanup via the goto */
2960 ast_free(leave_hook_data);
2961 ast_log(LOG_ERROR, "Couldn't add bridge leave hook for channel '%s'\n", ast_channel_name(chan));
2962 goto confbridge_cleanup;
2963 }
2964
2965 if (ast_bridge_join_hook(&user.features, join_callback, NULL, NULL, 0)) {
2967 }
2968
2970 chan,
2971 NULL,
2972 &user.features,
2973 &user.tech_args,
2974 0);
2975
2976 /* This is a catch-all in case joining the bridge failed or for some reason
2977 * an async announcement got queued up and hasn't been told to play yet
2978 */
2980
2981 if (!user.kicked && ast_check_hangup(chan)) {
2982 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "HANGUP");
2983 }
2984
2985 /* if we're shutting down, don't attempt to do further processing */
2986 if (ast_shutting_down()) {
2987 /*
2988 * Not taking any new calls at this time. We cannot create
2989 * the announcer channel if this is the first channel into
2990 * the conference and we certainly cannot create any
2991 * recording channel.
2992 */
2994 conference = NULL;
2995 goto confbridge_cleanup;
2996 }
2997
2998 /* If this user was a video source, we need to clean up and possibly pick a new source. */
3000
3001 /* if this user has a intro, play it when leaving */
3002 if (!quiet && !ast_strlen_zero(user.name_rec_location)) {
3003 async_play_sound_file(conference, user.name_rec_location, NULL);
3006 async_delete_name_rec(conference, user.name_rec_location);
3007 async_delete_task_pushed = 1;
3008 }
3009
3010 /* play the leave sound */
3011 if (!quiet) {
3012 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference->b_profile.sounds);
3013 async_play_sound_file(conference, leave_sound, NULL);
3014 }
3015
3016 /* If the user was kicked from the conference play back the audio prompt for it */
3017 if (!quiet && user.kicked) {
3018 res = ast_stream_and_wait(chan,
3020 "");
3021 }
3022
3023 /* Easy as pie, depart this channel from the conference bridge */
3025 conference = NULL;
3026
3027 /* Restore volume adjustments to previous values in case they were changed */
3028 if (volume_adjustments[0]) {
3029 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
3030 }
3031 if (volume_adjustments[1]) {
3032 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
3033 }
3034
3035confbridge_cleanup:
3036 if (!async_delete_task_pushed && !ast_strlen_zero(user.name_rec_location)) {
3037 ast_filedelete(user.name_rec_location, NULL);
3038 }
3041 return res;
3042}
3043
3045 struct confbridge_user *user,
3046 struct ast_bridge_channel *bridge_channel)
3047{
3048 int mute;
3049
3050 /* Toggle user level mute request. */
3051 mute = !user->muted;
3053
3054 return play_file(bridge_channel, NULL,
3057}
3058
3060 struct confbridge_user *user,
3061 struct ast_bridge_channel *bridge_channel)
3062{
3063 unsigned int binaural;
3064 ast_bridge_channel_lock_bridge(bridge_channel);
3065 binaural = !bridge_channel->binaural_suspended;
3066 bridge_channel->binaural_suspended = binaural;
3067 ast_bridge_unlock(bridge_channel->bridge);
3068 return play_file(bridge_channel, NULL, (binaural ?
3069 conf_get_sound(CONF_SOUND_BINAURAL_OFF, user->b_profile.sounds) :
3070 conf_get_sound(CONF_SOUND_BINAURAL_ON, user->b_profile.sounds))) < 0;
3071}
3072
3074{
3075 struct confbridge_user *cur_user = NULL;
3076 const char *sound_to_play;
3077 int mute;
3078
3080
3081 /* Toggle bridge level mute request. */
3082 mute = !conference->muted;
3083 conference->muted = mute;
3084
3086 if (!ast_test_flag(&cur_user->u_profile, USER_OPT_ADMIN)) {
3087 /* Set user level to bridge level mute request. */
3088 cur_user->muted = mute;
3089 conf_update_user_mute(cur_user);
3090 }
3091 }
3092
3094
3095 sound_to_play = conf_get_sound(
3098
3099 if (strcmp(conference->b_profile.language, ast_channel_language(user->chan))) {
3100 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
3101 ast_stream_and_wait(user->chan, sound_to_play, "");
3102
3103 /* Announce to the group that all participants are muted */
3105 play_sound_file(conference, sound_to_play);
3107 } else {
3108 /* Playing the sound asynchronously lets the sound be heard by everyone at once */
3109 async_play_sound_file(conference, sound_to_play, user->chan);
3110 }
3111
3112 return 0;
3113}
3114
3115static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
3116{
3117 char *file_copy = ast_strdupa(playback_file);
3118 char *file = NULL;
3119
3120 while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
3121 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
3122 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
3123 return -1;
3124 }
3125 }
3126 return 0;
3127}
3128
3130 struct confbridge_user *user,
3131 struct ast_bridge_channel *bridge_channel,
3132 struct conf_menu *menu,
3133 const char *playback_file,
3134 const char *cur_dtmf,
3135 int *stop_prompts)
3136{
3137 int i;
3138 int digit = 0;
3139 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
3140 struct conf_menu_entry new_menu_entry = { { 0, }, };
3141 char *file_copy = ast_strdupa(playback_file);
3142 char *file = NULL;
3143
3144 while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
3145 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
3146 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
3147 return -1;
3148 }
3149
3150 /* now wait for more digits. */
3151 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
3152 /* streaming finished and no DTMF was entered */
3153 continue;
3154 } else if (digit == -1) {
3155 /* error */
3156 return -1;
3157 } else {
3158 break; /* dtmf was entered */
3159 }
3160 }
3161 if (!digit) {
3162 /* streaming finished on all files and no DTMF was entered */
3163 return -1;
3164 }
3165 ast_stopstream(bridge_channel->chan);
3166
3167 /* If we get here, then DTMF has been entered, This means no
3168 * additional prompts should be played for this menu entry */
3169 *stop_prompts = 1;
3170
3171 /* If a digit was pressed during the payback, update
3172 * the dtmf string and look for a new menu entry in the
3173 * menu structure */
3174 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
3175 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
3176 dtmf[i] = cur_dtmf[i];
3177 if (!dtmf[i]) {
3178 dtmf[i] = (char) digit;
3179 dtmf[i + 1] = '\0';
3180 i = -1;
3181 break;
3182 }
3183 }
3184 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
3185 * If this is the case, no new DTMF sequence should be looked for. */
3186 if (i != -1) {
3187 return 0;
3188 }
3189
3190 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
3191 execute_menu_entry(conference,
3192 user,
3193 bridge_channel,
3194 &new_menu_entry, menu);
3195 conf_menu_entry_destroy(&new_menu_entry);
3196 }
3197 return 0;
3198}
3199
3200static int action_kick_last(struct confbridge_conference *conference,
3201 struct ast_bridge_channel *bridge_channel,
3202 struct confbridge_user *user)
3203{
3204 struct confbridge_user *last_user = NULL;
3205 int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
3206
3207 if (!isadmin) {
3208 play_file(bridge_channel, NULL,
3210 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
3211 ast_channel_name(bridge_channel->chan),
3212 conference->name);
3213 return -1;
3214 }
3215
3217 last_user = AST_LIST_LAST(&conference->active_list);
3218 if (!last_user) {
3220 return 0;
3221 }
3222
3223 if (last_user == user || ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN)) {
3225 play_file(bridge_channel, NULL,
3227 } else if (!last_user->kicked) {
3228 last_user->kicked = 1;
3229 pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
3232 }
3233
3234 return 0;
3235}
3236
3237static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
3238{
3239 struct ast_pbx_args args;
3240 struct ast_pbx *pbx;
3241 char *exten;
3242 char *context;
3243 int priority;
3244 int res;
3245
3246 memset(&args, 0, sizeof(args));
3247 args.no_hangup_chan = 1;
3248
3249 ast_channel_lock(bridge_channel->chan);
3250
3251 /*save off*/
3252 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
3253 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
3254 priority = ast_channel_priority(bridge_channel->chan);
3255 pbx = ast_channel_pbx(bridge_channel->chan);
3256 ast_channel_pbx_set(bridge_channel->chan, NULL);
3257
3258 /*set new*/
3259 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
3260 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
3261 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
3262
3263 ast_channel_unlock(bridge_channel->chan);
3264
3265 /*execute*/
3266 res = ast_pbx_run_args(bridge_channel->chan, &args);
3267
3268 /*restore*/
3269 ast_channel_lock(bridge_channel->chan);
3270
3271 ast_channel_exten_set(bridge_channel->chan, exten);
3272 ast_channel_context_set(bridge_channel->chan, context);
3273 ast_channel_priority_set(bridge_channel->chan, priority);
3274 ast_channel_pbx_set(bridge_channel->chan, pbx);
3275
3276 ast_channel_unlock(bridge_channel->chan);
3277
3278 return res;
3279}
3280
3281static int execute_menu_entry(struct confbridge_conference *conference,
3282 struct confbridge_user *user,
3283 struct ast_bridge_channel *bridge_channel,
3284 struct conf_menu_entry *menu_entry,
3285 struct conf_menu *menu)
3286{
3287 struct conf_menu_action *menu_action;
3288 int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
3289 int stop_prompts = 0;
3290 int res = 0;
3291
3292 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
3293 switch (menu_action->id) {
3295 res |= action_toggle_mute(conference, user, bridge_channel);
3296 break;
3298 action_toggle_binaural(conference, user, bridge_channel);
3299 break;
3301 if (!isadmin) {
3302 break;
3303 }
3305 break;
3307 announce_user_count(conference, user, bridge_channel);
3308 break;
3310 if (!stop_prompts) {
3311 res |= action_playback(bridge_channel, menu_action->data.playback_file);
3312 ast_test_suite_event_notify("CONF_MENU_PLAYBACK",
3313 "Message: %s\r\nChannel: %s",
3314 menu_action->data.playback_file, ast_channel_name(bridge_channel->chan));
3315 }
3316 break;
3319 break;
3322 break;
3326 break;
3330 break;
3334 break;
3338 break;
3340 if (!(stop_prompts)) {
3341 res |= action_playback_and_continue(conference,
3342 user,
3343 bridge_channel,
3344 menu,
3345 menu_action->data.playback_file,
3346 menu_entry->dtmf,
3347 &stop_prompts);
3348 }
3349 break;
3351 res |= action_dialplan_exec(bridge_channel, menu_action);
3352 break;
3354 if (!isadmin) {
3355 break;
3356 }
3357 conference->locked = (!conference->locked ? 1 : 0);
3358 res |= play_file(bridge_channel, NULL,
3361 conference->b_profile.sounds)) < 0;
3362 break;
3364 res |= action_kick_last(conference, bridge_channel, user);
3365 break;
3366 case MENU_ACTION_LEAVE:
3367 pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF");
3368 ao2_lock(conference);
3369 ast_bridge_remove(conference->bridge, bridge_channel->chan);
3370 ast_test_suite_event_notify("CONF_MENU_LEAVE",
3371 "Channel: %s",
3372 ast_channel_name(bridge_channel->chan));
3373 ao2_unlock(conference);
3374 break;
3375 case MENU_ACTION_NOOP:
3376 break;
3378 ao2_lock(conference);
3379 if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
3380 ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan);
3381 }
3382 ao2_unlock(conference);
3383 break;
3385 handle_video_on_exit(conference, bridge_channel->chan);
3386 break;
3387 }
3388 }
3389 return res;
3390}
3391
3392int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
3393 struct confbridge_user *user,
3394 struct conf_menu_entry *menu_entry,
3395 struct conf_menu *menu)
3396{
3397 /* See if music on hold is playing */
3399
3400 /* execute the list of actions associated with this menu entry */
3401 execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu);
3402
3403 /* See if music on hold needs to be started back up again */
3405
3406 async_play_sound_ready(bridge_channel->chan);
3407
3408 return 0;
3409}
3410
3412 const char *channel)
3413{
3414 int res = -1;
3415 int match;
3416 struct confbridge_user *user = NULL;
3417 int all = !strcasecmp("all", channel);
3418 int participants = !strcasecmp("participants", channel);
3419
3420 SCOPED_AO2LOCK(bridge_lock, conference);
3421
3423 if (user->kicked) {
3424 continue;
3425 }
3426 match = !strcasecmp(channel, ast_channel_name(user->chan));
3427 if (match || all
3428 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3429 user->kicked = 1;
3430 pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
3432 res = 0;
3433 if (match) {
3434 return res;
3435 }
3436 }
3437 }
3439 if (user->kicked) {
3440 continue;
3441 }
3442 match = !strcasecmp(channel, ast_channel_name(user->chan));
3443 if (match || all
3444 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3445 user->kicked = 1;
3446 pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
3448 res = 0;
3449 if (match) {
3450 return res;
3451 }
3452 }
3453 }
3454
3455 return res;
3456}
3457
3458static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
3459{
3460 int which = 0;
3461 struct confbridge_conference *conference;
3462 char *res = NULL;
3463 int wordlen = strlen(word);
3464 struct ao2_iterator iter;
3465
3467 while ((conference = ao2_iterator_next(&iter))) {
3468 if (!strncasecmp(conference->name, word, wordlen) && ++which > state) {
3469 res = ast_strdup(conference->name);
3470 ao2_ref(conference, -1);
3471 break;
3472 }
3473 ao2_ref(conference, -1);
3474 }
3475 ao2_iterator_destroy(&iter);
3476
3477 return res;
3478}
3479
3480static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
3481{
3482 int which = 0;
3483 RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
3484 struct confbridge_user *user;
3485 char *res = NULL;
3486 int wordlen = strlen(word);
3487
3488 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3489 if (!conference) {
3490 return NULL;
3491 }
3492
3493 if (!strncasecmp("all", word, wordlen) && ++which > state) {
3494 return ast_strdup("all");
3495 }
3496
3497 if (!strncasecmp("participants", word, wordlen) && ++which > state) {
3498 return ast_strdup("participants");
3499 }
3500
3501 {
3502 SCOPED_AO2LOCK(bridge_lock, conference);
3504 if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
3505 res = ast_strdup(ast_channel_name(user->chan));
3506 return res;
3507 }
3508 }
3510 if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
3511 res = ast_strdup(ast_channel_name(user->chan));
3512 return res;
3513 }
3514 }
3515 }
3516
3517 return NULL;
3518}
3519
3520static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3521{
3522 struct confbridge_conference *conference;
3523 int not_found;
3524
3525 switch (cmd) {
3526 case CLI_INIT:
3527 e->command = "confbridge kick";
3528 e->usage =
3529 "Usage: confbridge kick <conference> <channel>\n"
3530 " Kicks a channel out of the conference bridge.\n"
3531 " (all to kick everyone, participants to kick non-admins).\n";
3532 return NULL;
3533 case CLI_GENERATE:
3534 if (a->pos == 2) {
3535 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3536 }
3537 if (a->pos == 3) {
3538 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3539 }
3540 return NULL;
3541 }
3542
3543 if (a->argc != 4) {
3544 return CLI_SHOWUSAGE;
3545 }
3546
3547 conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
3548 if (!conference) {
3549 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3550 return CLI_SUCCESS;
3551 }
3552 not_found = kick_conference_participant(conference, a->argv[3]);
3553 ao2_ref(conference, -1);
3554 if (not_found) {
3555 if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
3556 ast_cli(a->fd, "No participants found!\n");
3557 } else {
3558 ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
3559 }
3560 return CLI_SUCCESS;
3561 }
3562 ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]);
3563 return CLI_SUCCESS;
3564}
3565
3566static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
3567{
3568 char flag_str[6 + 1];/* Max flags + terminator */
3569 int pos = 0;
3570
3571 /* Build flags column string. */
3572 if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
3573 flag_str[pos++] = 'A';
3574 }
3575 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
3576 flag_str[pos++] = 'M';
3577 }
3578 if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
3579 flag_str[pos++] = 'W';
3580 }
3581 if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) {
3582 flag_str[pos++] = 'E';
3583 }
3584 if (user->muted) {
3585 flag_str[pos++] = 'm';
3586 }
3587 if (waiting) {
3588 flag_str[pos++] = 'w';
3589 }
3590 flag_str[pos] = '\0';
3591
3592 ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n",
3593 ast_channel_name(user->chan),
3594 flag_str,
3595 user->u_profile.name,
3596 user->conference->b_profile.name,
3597 user->menu_name,
3599 ast_channel_caller(user->chan)->id.number.str, "<unknown>"));
3600}
3601
3602static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3603{
3604 struct confbridge_conference *conference;
3605
3606 switch (cmd) {
3607 case CLI_INIT:
3608 e->command = "confbridge list";
3609 e->usage =
3610 "Usage: confbridge list [<name>]\n"
3611 " Lists all currently active conference bridges or a specific conference bridge.\n"
3612 "\n"
3613 " When a conference bridge name is provided, flags may be shown for users. Below\n"
3614 " are the flags and what they represent.\n"
3615 "\n"
3616 " Flags:\n"
3617 " A - The user is an admin\n"
3618 " M - The user is a marked user\n"
3619 " W - The user must wait for a marked user to join\n"
3620 " E - The user will be kicked after the last marked user leaves the conference\n"
3621 " m - The user is muted\n"
3622 " w - The user is waiting for a marked user to join\n";
3623 return NULL;
3624 case CLI_GENERATE:
3625 if (a->pos == 2) {
3626 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3627 }
3628 return NULL;
3629 }
3630
3631 if (a->argc == 2) {
3632 struct ao2_iterator iter;
3633
3634 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked Muted\n");
3635 ast_cli(a->fd, "================================ ====== ====== ====== =====\n");
3637 while ((conference = ao2_iterator_next(&iter))) {
3638 ast_cli(a->fd, "%-32s %6u %6u %-6s %s\n",
3639 conference->name,
3640 conference->activeusers + conference->waitingusers,
3641 conference->markedusers,
3642 AST_CLI_YESNO(conference->locked),
3643 AST_CLI_YESNO(conference->muted));
3644 ao2_ref(conference, -1);
3645 }
3646 ao2_iterator_destroy(&iter);
3647 return CLI_SUCCESS;
3648 }
3649
3650 if (a->argc == 3) {
3651 struct confbridge_user *user;
3652
3654 if (!conference) {
3655 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3656 return CLI_SUCCESS;
3657 }
3658 ast_cli(a->fd, "Channel Flags User Profile Bridge Profile Menu CallerID\n");
3659 ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n");
3663 }
3666 }
3668 ao2_ref(conference, -1);
3669 return CLI_SUCCESS;
3670 }
3671
3672 return CLI_SHOWUSAGE;
3673}
3674
3675/*! \internal
3676 * \brief finds a conference by name and locks/unlocks.
3677 *
3678 * \retval 0 success
3679 * \retval -1 conference not found
3680 */
3681static int generic_lock_unlock_helper(int lock, const char *conference_name)
3682{
3683 struct confbridge_conference *conference;
3684 int res = 0;
3685
3686 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3687 if (!conference) {
3688 return -1;
3689 }
3690 ao2_lock(conference);
3691 conference->locked = lock;
3692 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name);
3693 ao2_unlock(conference);
3694 ao2_ref(conference, -1);
3695
3696 return res;
3697}
3698
3699/*! \internal
3700 * \brief finds a conference user by channel name and mutes/unmutes them.
3701 *
3702 * \retval 0 success
3703 * \retval -1 conference not found
3704 * \retval -2 user not found
3705 */
3706static int generic_mute_unmute_helper(int mute, const char *conference_name,
3707 const char *chan_name)
3708{
3709 RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
3710 struct confbridge_user *user;
3711 int all = !strcasecmp("all", chan_name);
3712 int participants = !strcasecmp("participants", chan_name);
3713 int res = -2;
3714
3715 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3716 if (!conference) {
3717 return -1;
3718 }
3719
3720 {
3721 SCOPED_AO2LOCK(bridge_lock, conference);
3723 int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
3724 strlen(chan_name));
3725 if (match || all
3726 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3728 res = 0;
3729 if (match) {
3730 return res;
3731 }
3732 }
3733 }
3734
3736 int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
3737 strlen(chan_name));
3738 if (match || all
3739 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
3741 res = 0;
3742 if (match) {
3743 return res;
3744 }
3745 }
3746 }
3747 }
3748
3749 return res;
3750}
3751
3752static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
3753{
3754 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
3755
3756 if (res == -1) {
3757 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
3758 return -1;
3759 } else if (res == -2) {
3760 if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
3761 ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]);
3762 } else {
3763 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
3764 }
3765 return -1;
3766 }
3767 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
3768 return 0;
3769}
3770
3771static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3772{
3773 switch (cmd) {
3774 case CLI_INIT:
3775 e->command = "confbridge mute";
3776 e->usage =
3777 "Usage: confbridge mute <conference> <channel>\n"
3778 " Mute a channel in a conference.\n"
3779 " (all to mute everyone, participants to mute non-admins)\n"
3780 " If the specified channel is a prefix,\n"
3781 " the action will be taken on the first\n"
3782 " matching channel.\n";
3783 return NULL;
3784 case CLI_GENERATE:
3785 if (a->pos == 2) {
3786 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3787 }
3788 if (a->pos == 3) {
3789 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3790 }
3791 return NULL;
3792 }
3793 if (a->argc != 4) {
3794 return CLI_SHOWUSAGE;
3795 }
3796
3798
3799 return CLI_SUCCESS;
3800}
3801
3802static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3803{
3804 switch (cmd) {
3805 case CLI_INIT:
3806 e->command = "confbridge unmute";
3807 e->usage =
3808 "Usage: confbridge unmute <conference> <channel>\n"
3809 " Unmute a channel in a conference.\n"
3810 " (all to unmute everyone, participants to unmute non-admins)\n"
3811 " If the specified channel is a prefix,\n"
3812 " the action will be taken on the first\n"
3813 " matching channel.\n";
3814 return NULL;
3815 case CLI_GENERATE:
3816 if (a->pos == 2) {
3817 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3818 }
3819 if (a->pos == 3) {
3820 return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
3821 }
3822 return NULL;
3823 }
3824 if (a->argc != 4) {
3825 return CLI_SHOWUSAGE;
3826 }
3827
3829
3830 return CLI_SUCCESS;
3831}
3832
3833static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3834{
3835 switch (cmd) {
3836 case CLI_INIT:
3837 e->command = "confbridge lock";
3838 e->usage =
3839 "Usage: confbridge lock <conference>\n"
3840 " Lock a conference. While locked, no new non-admins\n"
3841 " may join the conference.\n";
3842 return NULL;
3843 case CLI_GENERATE:
3844 if (a->pos == 2) {
3845 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3846 }
3847 return NULL;
3848 }
3849 if (a->argc != 3) {
3850 return CLI_SHOWUSAGE;
3851 }
3852 if (generic_lock_unlock_helper(1, a->argv[2])) {
3853 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
3854 } else {
3855 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
3856 }
3857 return CLI_SUCCESS;
3858}
3859
3860static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3861{
3862 switch (cmd) {
3863 case CLI_INIT:
3864 e->command = "confbridge unlock";
3865 e->usage =
3866 "Usage: confbridge unlock <conference>\n"
3867 " Unlock a previously locked conference.\n";
3868 return NULL;
3869 case CLI_GENERATE:
3870 if (a->pos == 2) {
3871 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3872 }
3873 return NULL;
3874 }
3875 if (a->argc != 3) {
3876 return CLI_SHOWUSAGE;
3877 }
3878 if (generic_lock_unlock_helper(0, a->argv[2])) {
3879 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
3880 } else {
3881 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
3882 }
3883 return CLI_SUCCESS;
3884}
3885
3886static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3887{
3888 const char *rec_file = NULL;
3889 struct confbridge_conference *conference;
3890
3891 switch (cmd) {
3892 case CLI_INIT:
3893 e->command = "confbridge record start";
3894 e->usage =
3895 "Usage: confbridge record start <conference> <file>\n"
3896 " <file> is optional, Otherwise the bridge profile\n"
3897 " record file will be used. If the bridge profile\n"
3898 " has no record file specified, a file will automatically\n"
3899 " be generated in the monitor directory\n";
3900 return NULL;
3901 case CLI_GENERATE:
3902 if (a->pos == 3) {
3903 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3904 }
3905 return NULL;
3906 }
3907 if (a->argc < 4) {
3908 return CLI_SHOWUSAGE;
3909 }
3910 if (a->argc == 5) {
3911 rec_file = a->argv[4];
3912 }
3913
3914 conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
3915 if (!conference) {
3916 ast_cli(a->fd, "Conference not found.\n");
3917 return CLI_FAILURE;
3918 }
3919 ao2_lock(conference);
3920 if (conf_is_recording(conference)) {
3921 ast_cli(a->fd, "Conference is already being recorded.\n");
3922 ao2_unlock(conference);
3923 ao2_ref(conference, -1);
3924 return CLI_SUCCESS;
3925 }
3926 if (!ast_strlen_zero(rec_file)) {
3927 ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
3928 }
3929
3930 if (conf_start_record(conference)) {
3931 ast_cli(a->fd, "Could not start recording due to internal error.\n");
3932 ao2_unlock(conference);
3933 ao2_ref(conference, -1);
3934 return CLI_FAILURE;
3935 }
3936 ao2_unlock(conference);
3937
3938 ast_cli(a->fd, "Recording started\n");
3939 ao2_ref(conference, -1);
3940 return CLI_SUCCESS;
3941}
3942
3943static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3944{
3945 struct confbridge_conference *conference;
3946 int ret;
3947
3948 switch (cmd) {
3949 case CLI_INIT:
3950 e->command = "confbridge record stop";
3951 e->usage =
3952 "Usage: confbridge record stop <conference>\n"
3953 " Stop a previously started recording.\n";
3954 return NULL;
3955 case CLI_GENERATE:
3956 if (a->pos == 3) {
3957 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
3958 }
3959 return NULL;
3960 }
3961 if (a->argc != 4) {
3962 return CLI_SHOWUSAGE;
3963 }
3964
3965 conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
3966 if (!conference) {
3967 ast_cli(a->fd, "Conference not found.\n");
3968 return CLI_SUCCESS;
3969 }
3970 ao2_lock(conference);
3971 ret = conf_stop_record(conference);
3972 ao2_unlock(conference);
3973 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
3974 ao2_ref(conference, -1);
3975 return CLI_SUCCESS;
3976}
3977
3979 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
3980 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
3981 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."),
3982 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."),
3983 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
3984 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
3985 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
3986 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
3987};
3989 .name = "CONFBRIDGE",
3990 .write = func_confbridge_helper,
3991};
3992
3993static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
3995 .name = "CONFBRIDGE_INFO",
3996 .read = func_confbridge_info,
3997};
3998
3999static int func_confbridge_channels(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4000{
4001 char *parse, *outbuf;
4002 struct confbridge_conference *conference;
4003 struct confbridge_user *user;
4004 int bytes, count = 0;
4005 size_t outlen;
4008 AST_APP_ARG(confno);
4009 );
4010
4011 /* parse all the required arguments and make sure they exist. */
4012 if (ast_strlen_zero(data)) {
4013 return -1;
4014 }
4015 parse = ast_strdupa(data);
4017 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
4018 ast_log(LOG_WARNING, "Usage: %s(category,confno)", cmd);
4019 return -1;
4020 }
4022 if (!conference) {
4023 ast_debug(1, "No such conference: %s\n", args.confno);
4024 return -1;
4025 }
4026
4027 outbuf = buf;
4028 outlen = len;
4029
4031 if (!strcasecmp(args.type, "parties")) {
4033 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4034 outbuf += bytes;
4035 outlen -= bytes;
4036 }
4038 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4039 outbuf += bytes;
4040 outlen -= bytes;
4041 }
4042 } else if (!strcasecmp(args.type, "active")) {
4044 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4045 outbuf += bytes;
4046 outlen -= bytes;
4047 }
4048 } else if (!strcasecmp(args.type, "waiting")) {
4050 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4051 outbuf += bytes;
4052 outlen -= bytes;
4053 }
4054 } else if (!strcasecmp(args.type, "admins")) {
4056 if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
4057 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4058 outbuf += bytes;
4059 outlen -= bytes;
4060 }
4061 }
4062 } else if (!strcasecmp(args.type, "marked")) {
4064 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
4065 bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
4066 outbuf += bytes;
4067 outlen -= bytes;
4068 }
4069 }
4070 } else {
4071 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to %s.\n", args.type, cmd);
4072 }
4074 ao2_ref(conference, -1);
4075 return 0;
4076}
4077
4079 .name = "CONFBRIDGE_CHANNELS",
4081};
4082
4083static int action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
4084{
4085 struct ast_channel_snapshot *snapshot;
4086 struct ast_str *snap_str;
4087
4089 if (!snapshot) {
4090 return 0;
4091 }
4092
4093 snap_str = ast_manager_build_channel_state_string(snapshot);
4094 if (!snap_str) {
4095 ao2_ref(snapshot, -1);
4096 return 0;
4097 }
4098
4099 astman_append(s,
4100 "Event: ConfbridgeList\r\n"
4101 "%s"
4102 "Conference: %s\r\n"
4103 "Admin: %s\r\n"
4104 "MarkedUser: %s\r\n"
4105 "WaitMarked: %s\r\n"
4106 "EndMarked: %s\r\n"
4107 "EndMarkedAny: %s\r\n"
4108 "Waiting: %s\r\n"
4109 "Muted: %s\r\n"
4110 "Talking: %s\r\n"
4111 "AnsweredTime: %d\r\n"
4112 "%s"
4113 "\r\n",
4114 id_text,
4115 conference->name,
4121 AST_YESNO(waiting),
4122 AST_YESNO(user->muted),
4123 AST_YESNO(user->talking),
4125 ast_str_buffer(snap_str));
4126
4127 ast_free(snap_str);
4128 ao2_ref(snapshot, -1);
4129
4130 return 1;
4131}
4132
4133static int action_confbridgelist(struct mansession *s, const struct message *m)
4134{
4135 const char *actionid = astman_get_header(m, "ActionID");
4136 const char *conference_name = astman_get_header(m, "Conference");
4137 struct confbridge_user *user;
4138 struct confbridge_conference *conference;
4139 char id_text[80];
4140 int total = 0;
4141
4142 id_text[0] = '\0';
4143 if (!ast_strlen_zero(actionid)) {
4144 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
4145 }
4146 if (ast_strlen_zero(conference_name)) {
4147 astman_send_error(s, m, "No Conference name provided.");
4148 return 0;
4149 }
4151 astman_send_error(s, m, "No active conferences.");
4152 return 0;
4153 }
4154 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4155 if (!conference) {
4156 astman_send_error(s, m, "No Conference by that name found.");
4157 return 0;
4158 }
4159
4160 astman_send_listack(s, m, "Confbridge user list will follow", "start");
4161
4162 ao2_lock(conference);
4163 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4164 total += action_confbridgelist_item(s, id_text, conference, user, 0);
4165 }
4166 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
4167 total += action_confbridgelist_item(s, id_text, conference, user, 1);
4168 }
4169 ao2_unlock(conference);
4170 ao2_ref(conference, -1);
4171
4172 astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
4174
4175 return 0;
4176}
4177
4178static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
4179{
4180 const char *actionid = astman_get_header(m, "ActionID");
4181 struct confbridge_conference *conference;
4182 struct ao2_iterator iter;
4183 char id_text[512] = "";
4184 int totalitems = 0;
4185
4186 if (!ast_strlen_zero(actionid)) {
4187 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
4188 }
4189
4191 astman_send_error(s, m, "No active conferences.");
4192 return 0;
4193 }
4194
4195 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
4196
4197 /* Traverse the conference list */
4199 while ((conference = ao2_iterator_next(&iter))) {
4200 totalitems++;
4201
4202 ao2_lock(conference);
4203 astman_append(s,
4204 "Event: ConfbridgeListRooms\r\n"
4205 "%s"
4206 "Conference: %s\r\n"
4207 "Parties: %u\r\n"
4208 "Marked: %u\r\n"
4209 "Locked: %s\r\n"
4210 "Muted: %s\r\n"
4211 "\r\n",
4212 id_text,
4213 conference->name,
4214 conference->activeusers + conference->waitingusers,
4215 conference->markedusers,
4216 AST_YESNO(conference->locked),
4217 AST_YESNO(conference->muted));
4218 ao2_unlock(conference);
4219
4220 ao2_ref(conference, -1);
4221 }
4222 ao2_iterator_destroy(&iter);
4223
4224 /* Send final confirmation */
4225 astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
4227 return 0;
4228}
4229
4230static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
4231{
4232 const char *conference_name = astman_get_header(m, "Conference");
4233 const char *channel_name = astman_get_header(m, "Channel");
4234 int res = 0;
4235
4236 if (ast_strlen_zero(conference_name)) {
4237 astman_send_error(s, m, "No Conference name provided.");
4238 return 0;
4239 }
4240 if (ast_strlen_zero(channel_name)) {
4241 astman_send_error(s, m, "No channel name provided.");
4242 return 0;
4243 }
4245 astman_send_error(s, m, "No active conferences.");
4246 return 0;
4247 }
4248
4249 res = generic_mute_unmute_helper(mute, conference_name, channel_name);
4250
4251 if (res == -1) {
4252 astman_send_error(s, m, "No Conference by that name found.");
4253 return 0;
4254 } else if (res == -2) {
4255 astman_send_error(s, m, "No Channel by that name found in Conference.");
4256 return 0;
4257 }
4258
4259 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
4260 return 0;
4261}
4262
4263static int action_confbridgeunmute(struct mansession *s, const struct message *m)
4264{
4265 return action_mute_unmute_helper(s, m, 0);
4266}
4267static int action_confbridgemute(struct mansession *s, const struct message *m)
4268{
4269 return action_mute_unmute_helper(s, m, 1);
4270}
4271
4272static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
4273{
4274 const char *conference_name = astman_get_header(m, "Conference");
4275 int res = 0;
4276
4277 if (ast_strlen_zero(conference_name)) {
4278 astman_send_error(s, m, "No Conference name provided.");
4279 return 0;
4280 }
4282 astman_send_error(s, m, "No active conferences.");
4283 return 0;
4284 }
4285 if ((res = generic_lock_unlock_helper(lock, conference_name))) {
4286 astman_send_error(s, m, "No Conference by that name found.");
4287 return 0;
4288 }
4289 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
4290 return 0;
4291}
4292static int action_confbridgeunlock(struct mansession *s, const struct message *m)
4293{
4294 return action_lock_unlock_helper(s, m, 0);
4295}
4296static int action_confbridgelock(struct mansession *s, const struct message *m)
4297{
4298 return action_lock_unlock_helper(s, m, 1);
4299}
4300
4301static int action_confbridgekick(struct mansession *s, const struct message *m)
4302{
4303 const char *conference_name = astman_get_header(m, "Conference");
4304 const char *channel = astman_get_header(m, "Channel");
4305 struct confbridge_conference *conference;
4306 int found;
4307
4308 if (ast_strlen_zero(conference_name)) {
4309 astman_send_error(s, m, "No Conference name provided.");
4310 return 0;
4311 }
4313 astman_send_error(s, m, "No active conferences.");
4314 return 0;
4315 }
4316
4317 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4318 if (!conference) {
4319 astman_send_error(s, m, "No Conference by that name found.");
4320 return 0;
4321 }
4322
4323 found = !kick_conference_participant(conference, channel);
4324 ao2_ref(conference, -1);
4325
4326 if (found) {
4327 astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked");
4328 } else {
4329 astman_send_error(s, m, "No Channel by that name found in Conference.");
4330 }
4331 return 0;
4332}
4333
4334static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
4335{
4336 const char *conference_name = astman_get_header(m, "Conference");
4337 const char *recordfile = astman_get_header(m, "RecordFile");
4338 struct confbridge_conference *conference;
4339
4340 if (ast_strlen_zero(conference_name)) {
4341 astman_send_error(s, m, "No Conference name provided.");
4342 return 0;
4343 }
4345 astman_send_error(s, m, "No active conferences.");
4346 return 0;
4347 }
4348
4349 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4350 if (!conference) {
4351 astman_send_error(s, m, "No Conference by that name found.");
4352 return 0;
4353 }
4354
4355 ao2_lock(conference);
4356 if (conf_is_recording(conference)) {
4357 astman_send_error(s, m, "Conference is already being recorded.");
4358 ao2_unlock(conference);
4359 ao2_ref(conference, -1);
4360 return 0;
4361 }
4362
4363 if (!ast_strlen_zero(recordfile)) {
4364 ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
4365 }
4366
4367 if (conf_start_record(conference)) {
4368 astman_send_error(s, m, "Internal error starting conference recording.");
4369 ao2_unlock(conference);
4370 ao2_ref(conference, -1);
4371 return 0;
4372 }
4373 ao2_unlock(conference);
4374
4375 ao2_ref(conference, -1);
4376 astman_send_ack(s, m, "Conference Recording Started.");
4377 return 0;
4378}
4379static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
4380{
4381 const char *conference_name = astman_get_header(m, "Conference");
4382 struct confbridge_conference *conference;
4383
4384 if (ast_strlen_zero(conference_name)) {
4385 astman_send_error(s, m, "No Conference name provided.");
4386 return 0;
4387 }
4389 astman_send_error(s, m, "No active conferences.");
4390 return 0;
4391 }
4392
4393 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4394 if (!conference) {
4395 astman_send_error(s, m, "No Conference by that name found.");
4396 return 0;
4397 }
4398
4399 ao2_lock(conference);
4400 if (conf_stop_record(conference)) {
4401 ao2_unlock(conference);
4402 astman_send_error(s, m, "Internal error while stopping recording.");
4403 ao2_ref(conference, -1);
4404 return 0;
4405 }
4406 ao2_unlock(conference);
4407
4408 ao2_ref(conference, -1);
4409 astman_send_ack(s, m, "Conference Recording Stopped.");
4410 return 0;
4411}
4412
4413static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
4414{
4415 const char *conference_name = astman_get_header(m, "Conference");
4416 const char *channel = astman_get_header(m, "Channel");
4417 struct confbridge_user *user;
4418 struct confbridge_conference *conference;
4419
4420 if (ast_strlen_zero(conference_name)) {
4421 astman_send_error(s, m, "No Conference name provided.");
4422 return 0;
4423 }
4424 if (ast_strlen_zero(channel)) {
4425 astman_send_error(s, m, "No channel name provided.");
4426 return 0;
4427 }
4429 astman_send_error(s, m, "No active conferences.");
4430 return 0;
4431 }
4432
4433 conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
4434 if (!conference) {
4435 astman_send_error(s, m, "No Conference by that name found.");
4436 return 0;
4437 }
4438
4439 /* find channel and set as video src. */
4440 ao2_lock(conference);
4441 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
4442 if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) {
4444 break;
4445 }
4446 }
4447 ao2_unlock(conference);
4448 ao2_ref(conference, -1);
4449
4450 /* do not access user after conference unlock. We are just
4451 * using this check to see if it was found or not */
4452 if (!user) {
4453 astman_send_error(s, m, "No channel by that name found in conference.");
4454 return 0;
4455 }
4456 astman_send_ack(s, m, "Conference single video source set.");
4457 return 0;
4458}
4459
4460static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4461{
4462 char *parse;
4463 struct confbridge_conference *conference;
4464 struct confbridge_user *user;
4465 int count = 0;
4468 AST_APP_ARG(confno);
4469 );
4470
4471 /* parse all the required arguments and make sure they exist. */
4472 if (ast_strlen_zero(data)) {
4473 return -1;
4474 }
4475 parse = ast_strdupa(data);
4477 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
4478 return -1;
4479 }
4481 if (!conference) {
4482 snprintf(buf, len, "0");
4483 return 0;
4484 }
4485
4486 /* get the correct count for the type requested */
4488 if (!strcasecmp(args.type, "parties")) {
4490 count++;
4491 }
4493 count++;
4494 }
4495 } else if (!strcasecmp(args.type, "admins")) {
4497 if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
4498 count++;
4499 }
4500 }
4501 } else if (!strcasecmp(args.type, "marked")) {
4503 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
4504 count++;
4505 }
4506 }
4507 } else if (!strcasecmp(args.type, "locked")) {
4508 count = conference->locked;
4509 } else if (!strcasecmp(args.type, "muted")) {
4510 count = conference->muted;
4511 } else {
4512 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.\n", args.type);
4513 }
4514 snprintf(buf, len, "%d", count);
4516 ao2_ref(conference, -1);
4517 return 0;
4518}
4519
4520static int confkick_exec(struct ast_channel *chan, const char *data)
4521{
4522 char *parse;
4523 struct confbridge_conference *conference;
4524 int not_found;
4525
4527 AST_APP_ARG(confbridge);
4528 AST_APP_ARG(channel);
4529 );
4530
4531 if (ast_strlen_zero(data)) {
4532 ast_log(LOG_WARNING, "No conference bridge specified.\n");
4533 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4534 return 0;
4535 }
4536
4537 parse = ast_strdupa(data);
4539
4540 conference = ao2_find(conference_bridges, args.confbridge, OBJ_KEY);
4541 if (!conference) {
4542 ast_log(LOG_WARNING, "No conference bridge named '%s' found!\n", args.confbridge);
4543 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4544 return 0;
4545 }
4546 if (ast_strlen_zero(args.channel)) {
4547 not_found = kick_conference_participant(conference, "all");
4548 } else {
4549 not_found = kick_conference_participant(conference, args.channel);
4550 }
4551
4552 ao2_ref(conference, -1);
4553 if (not_found) {
4554 if (ast_strlen_zero(args.channel) || !strcasecmp("all", args.channel) || !strcasecmp("participants", args.channel)) {
4555 ast_log(LOG_WARNING, "No participants found in conference bridge '%s'!\n", args.confbridge);
4556 } else {
4557 ast_log(LOG_WARNING, "No participant named '%s' found in conference bridge '%s'!\n", args.channel, args.confbridge);
4558 }
4559 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "FAILURE");
4560 return 0;
4561 }
4562 ast_debug(1, "Kicked '%s' out of conference '%s'\n", args.channel, args.confbridge);
4563 pbx_builtin_setvar_helper(chan, "CONFKICKSTATUS", "SUCCESS");
4564 return 0;
4565}
4566
4568{
4569 AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
4570 conference->activeusers++;
4571}
4572
4574{
4575 AST_LIST_INSERT_TAIL(&conference->active_list, user, list);
4576 conference->activeusers++;
4577 conference->markedusers++;
4578}
4579
4581{
4582 AST_LIST_INSERT_TAIL(&conference->waiting_list, user, list);
4583 conference->waitingusers++;
4584}
4585
4587{
4588 AST_LIST_REMOVE(&conference->active_list, user, list);
4589 conference->activeusers--;
4590}
4591
4593{
4594 AST_LIST_REMOVE(&conference->active_list, user, list);
4595 conference->activeusers--;
4596 conference->markedusers--;
4597}
4598
4600{
4602
4603 /* Turn on MOH if the single participant is set up for it */
4604 if (ast_test_flag(&only_user->u_profile, USER_OPT_MUSICONHOLD)) {
4605 conf_moh_start(only_user);
4606 }
4607 conf_update_user_mute(only_user);
4608}
4609
4615
4616/*!
4617 * \internal
4618 * \brief Unregister a ConfBridge channel technology.
4619 * \since 12.0.0
4620 *
4621 * \param tech What to unregister.
4622 */
4624{
4627}
4628
4629/*!
4630 * \internal
4631 * \brief Register a ConfBridge channel technology.
4632 * \since 12.0.0
4633 *
4634 * \param tech What to register.
4635 *
4636 * \retval 0 on success.
4637 * \retval -1 on error.
4638 */
4640{
4642 if (!tech->capabilities) {
4643 return -1;
4644 }
4646 if (ast_channel_register(tech)) {
4647 ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n",
4648 tech->type, tech->description);
4649 return -1;
4650 }
4651 return 0;
4652}
4653
4654/*! \brief Called when module is being unloaded */
4655static int unload_module(void)
4656{
4659
4663
4665
4666 ast_manager_unregister("ConfbridgeList");
4667 ast_manager_unregister("ConfbridgeListRooms");
4668 ast_manager_unregister("ConfbridgeMute");
4669 ast_manager_unregister("ConfbridgeUnmute");
4670 ast_manager_unregister("ConfbridgeKick");
4671 ast_manager_unregister("ConfbridgeUnlock");
4672 ast_manager_unregister("ConfbridgeLock");
4673 ast_manager_unregister("ConfbridgeStartRecord");
4674 ast_manager_unregister("ConfbridgeStopRecord");
4675 ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
4676
4677 /* Unsubscribe from stasis confbridge message type and clean it up. */
4679
4680 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
4683
4685
4688
4689 return 0;
4690}
4691
4692/*!
4693 * \brief Load the module
4694 *
4695 * Module loading including tests for configuration or dependencies.
4696 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
4697 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
4698 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
4699 * configuration file or other non-critical problem return
4700 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
4701 */
4702static int load_module(void)
4703{
4704 int res = 0;
4705
4706 if (conf_load_config()) {
4707 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
4709 }
4710
4713 unload_module();
4715 }
4716
4717 /* Create a container to hold the conference bridges */
4721 if (!conference_bridges) {
4722 unload_module();
4724 }
4725
4726 /* Setup manager stasis subscriptions */
4727 res |= manager_confbridge_init();
4728
4731
4735
4737
4747 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
4748 if (res) {
4749 unload_module();
4751 }
4752
4754}
4755
4756static int reload(void)
4757{
4758 return conf_reload_config();
4759}
4760
4761AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
4762 .support_level = AST_MODULE_SUPPORT_CORE,
4763 .load = load_module,
4764 .unload = unload_module,
4765 .reload = reload,
4766 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
4767 .optional_modules = "codec_speex,func_jitterbuffer",
static int quiet
Definition ael_main.c:123
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
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:1889
#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:1601
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:1626
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:1576
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:4016
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:3975
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:1019
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition bridge.h:491
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:1820
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:3914
@ 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:3983
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:3928
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:3991
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition bridge.c:1029
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:2077
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:4007
int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
Remove a channel from a bridge.
Definition bridge.c:2150
@ 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:3216
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:3958
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:3907
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:4126
int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
Unsuspend a channel from a bridge.
Definition bridge.c:3237
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:4175
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition bridge.h:480
@ AST_BRIDGE_IMPART_CHAN_INDEPENDENT
Definition bridge.h:600
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:4100
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:3998
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:3921
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:3829
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:3528
@ 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:3504
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:3484
struct ast_bridge_features * ast_bridge_features_new(void)
Allocate a new bridge features struct.
Definition bridge.c:3892
@ 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:3494
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition bridge.c:3862
void ast_bridge_features_destroy(struct ast_bridge_features *features)
Destroy an allocated bridge features struct.
Definition bridge.c:3883
@ 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:2401
static const char type[]
static int hangup(void *data)
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...
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition channel.c:2376
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition channel.c:2540
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:2983
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition channel.c:571
int ast_channel_priority(const struct ast_channel *chan)
#define ast_channel_ref(c)
Increase channel reference count.
Definition channel.h:3008
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:1171
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...
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition channel.c:446
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:540
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3019
int ast_channel_get_up_time(struct ast_channel *chan)
Obtain how long it has been since the channel was answered.
Definition channel.c:2845
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:3030
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition channel.c:2805
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:6373
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition channel.h:2984
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:2390
ast_channel_state
ast_channel states
@ AST_STATE_UP
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
#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:170
@ CONF_SOUND_JOIN
Definition confbridge.h:189
@ CONF_SOUND_OTHER_IN_PARTY
Definition confbridge.h:178
@ CONF_SOUND_INVALID_PIN
Definition confbridge.h:183
@ CONF_SOUND_LOCKED_NOW
Definition confbridge.h:186
@ CONF_SOUND_THERE_ARE
Definition confbridge.h:177
@ CONF_SOUND_ONLY_ONE
Definition confbridge.h:176
@ CONF_SOUND_GET_PIN
Definition confbridge.h:182
@ CONF_SOUND_ERROR_MENU
Definition confbridge.h:188
@ CONF_SOUND_PARTICIPANTS_UNMUTED
Definition confbridge.h:192
@ CONF_SOUND_BINAURAL_ON
Definition confbridge.h:194
@ CONF_SOUND_ONLY_PERSON
Definition confbridge.h:184
@ CONF_SOUND_BEGIN
Definition confbridge.h:193
@ CONF_SOUND_PARTICIPANTS_MUTED
Definition confbridge.h:191
@ CONF_SOUND_WAIT_FOR_LEADER
Definition confbridge.h:180
@ CONF_SOUND_HAS_LEFT
Definition confbridge.h:172
@ CONF_SOUND_BINAURAL_OFF
Definition confbridge.h:195
@ CONF_SOUND_LEAVE
Definition confbridge.h:190
@ CONF_SOUND_LEADER_HAS_LEFT
Definition confbridge.h:181
@ CONF_SOUND_PLACE_IN_CONF
Definition confbridge.h:179
@ CONF_SOUND_HAS_JOINED
Definition confbridge.h:171
@ CONF_SOUND_UNLOCKED_NOW
Definition confbridge.h:187
@ CONF_SOUND_UNMUTED
Definition confbridge.h:175
@ CONF_SOUND_MUTED
Definition confbridge.h:174
@ CONF_SOUND_LOCKED
Definition confbridge.h:185
@ CONF_SOUND_KICKED
Definition confbridge.h:173
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_ANNOUNCEMENTS
Definition confbridge.h:94
@ 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:113
@ MENU_ACTION_SET_SINGLE_VIDEO_SRC
Definition confbridge.h:112
@ MENU_ACTION_ADMIN_KICK_LAST
Definition confbridge.h:109
@ MENU_ACTION_TOGGLE_BINAURAL
Definition confbridge.h:116
@ MENU_ACTION_PLAYBACK
Definition confbridge.h:99
@ MENU_ACTION_LEAVE
Definition confbridge.h:110
@ MENU_ACTION_RESET_LISTENING
Definition confbridge.h:103
@ MENU_ACTION_INCREASE_TALKING
Definition confbridge.h:105
@ MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS
Definition confbridge.h:115
@ MENU_ACTION_INCREASE_LISTENING
Definition confbridge.h:101
@ MENU_ACTION_DIALPLAN_EXEC
Definition confbridge.h:107
@ MENU_ACTION_DECREASE_LISTENING
Definition confbridge.h:102
@ MENU_ACTION_PLAYBACK_AND_CONTINUE
Definition confbridge.h:100
@ MENU_ACTION_ADMIN_TOGGLE_LOCK
Definition confbridge.h:108
@ MENU_ACTION_DECREASE_TALKING
Definition confbridge.h:106
@ MENU_ACTION_PARTICIPANT_COUNT
Definition confbridge.h:114
@ MENU_ACTION_TOGGLE_MUTE
Definition confbridge.h:98
@ MENU_ACTION_NOOP
Definition confbridge.h:111
@ MENU_ACTION_RESET_TALKING
Definition confbridge.h:104
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.
@ AST_DEVICE_INUSE
Definition devicestate.h:55
@ THRESHOLD_SILENCE
Definition dsp.h:75
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition dsp.c:2196
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:223
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition file.c:1320
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:1920
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition file.c:1156
#define AST_DIGIT_NONE
Definition file.h:47
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition file.c:1168
#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:1882
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
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:1963
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:1921
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:1999
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition manager.c:1953
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1582
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2007
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1842
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7782
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:151
#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:2155
#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:2647
#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.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Asterisk locking-related definitions:
#define ast_cond_destroy(cond)
Definition lock.h:209
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition lock.h:611
#define ast_cond_wait(cond, mutex)
Definition lock.h:212
#define ast_cond_init(cond, attr)
Definition lock.h:208
#define ast_mutex_init(pmutex)
Definition lock.h:193
#define ast_mutex_unlock(a)
Definition lock.h:197
pthread_cond_t ast_cond_t
Definition lock.h:185
#define ast_mutex_destroy(a)
Definition lock.h:195
#define ast_mutex_lock(a)
Definition lock.h:196
#define ast_cond_signal(cond)
Definition lock.h:210
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:193
#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:404
@ 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:7802
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition channel.c:7812
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_SPOOL_DIR
Definition options.c:155
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:5206
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:3295
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:2735
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition pbx_app.c:483
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:3508
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition pbx.h:1572
#define ast_custom_function_register(acf)
Register a custom function.
Definition pbx.h:1563
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:1556
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 struct @523 args
#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:8411
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:1589
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
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#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
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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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:1869
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.
struct ast_frame_subclass subclass
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition lock.h:142
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:409
Definition pbx.h:216
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:225
const ast_string_field invalidpin
Definition confbridge.h:225
const ast_string_field placeintoconf
Definition confbridge.h:225
const ast_string_field onlyone
Definition confbridge.h:225
const ast_string_field otherinparty
Definition confbridge.h:225
const ast_string_field unlockednow
Definition confbridge.h:225
const ast_string_field binauralon
Definition confbridge.h:225
const ast_string_field getpin
Definition confbridge.h:225
const ast_string_field errormenu
Definition confbridge.h:225
const ast_string_field participantsunmuted
Definition confbridge.h:225
const ast_string_field participantsmuted
Definition confbridge.h:225
const ast_string_field leave
Definition confbridge.h:225
const ast_string_field locked
Definition confbridge.h:225
const ast_string_field join
Definition confbridge.h:225
const ast_string_field kicked
Definition confbridge.h:225
const ast_string_field hasleft
Definition confbridge.h:225
const ast_string_field unmuted
Definition confbridge.h:225
const ast_string_field leaderhasleft
Definition confbridge.h:225
const ast_string_field lockednow
Definition confbridge.h:225
const ast_string_field hasjoin
Definition confbridge.h:225
const ast_string_field binauraloff
Definition confbridge.h:225
const ast_string_field onlyperson
Definition confbridge.h:225
const ast_string_field thereare
Definition confbridge.h:225
const ast_string_field muted
Definition confbridge.h:225
const ast_string_field begin
Definition confbridge.h:225
char rec_options[128]
Definition confbridge.h:232
unsigned int remb_send_interval
Definition confbridge.h:242
unsigned int maximum_sample_rate
Definition confbridge.h:237
unsigned int remb_estimated_bitrate
Definition confbridge.h:243
char regcontext[AST_MAX_CONTEXT]
Definition confbridge.h:240
char rec_command[128]
Definition confbridge.h:233
unsigned int mix_interval
Definition confbridge.h:238
unsigned int video_update_discard
Definition confbridge.h:241
unsigned int internal_sample_rate
Definition confbridge.h:236
char rec_file[PATH_MAX]
Definition confbridge.h:231
char language[MAX_LANGUAGE]
Definition confbridge.h:230
unsigned int max_members
Definition confbridge.h:235
char name[MAX_PROFILE_NAME]
Definition confbridge.h:229
struct bridge_profile_sounds * sounds
Definition confbridge.h:239
enum conf_menu_action_id id
Definition confbridge.h:124
char exten[AST_MAX_EXTENSION]
Definition confbridge.h:129
struct conf_menu_action::@89 action
struct conf_menu_action::@88::@90 dialplan_args
char playback_file[PATH_MAX]
Definition confbridge.h:126
char context[AST_MAX_CONTEXT]
Definition confbridge.h:128
union conf_menu_action::@88 data
Definition confbridge.h:139
char dtmf[MAXIMUM_DTMF_FEATURE_STRING]
Definition confbridge.h:141
struct conf_menu_entry::@91 actions
The structure that represents a conference bridge.
Definition confbridge.h:247
char name[MAX_CONF_NAME]
Definition confbridge.h:248
struct confbridge_state * state
Definition confbridge.h:249
struct ast_str * orig_rec_file
Definition confbridge.h:260
struct ast_str * record_filename
Definition confbridge.h:259
struct ast_bridge * bridge
Definition confbridge.h:250
unsigned int activeusers
Definition confbridge.h:252
struct ast_channel * playback_chan
Definition confbridge.h:257
unsigned int waitingusers
Definition confbridge.h:254
struct ast_taskprocessor * playback_queue
Definition confbridge.h:263
struct bridge_profile b_profile
Definition confbridge.h:251
unsigned int markedusers
Definition confbridge.h:253
struct confbridge_conference::@94 active_list
struct ast_channel * record_chan
Definition confbridge.h:258
struct confbridge_conference::@95 waiting_list
unsigned int locked
Definition confbridge.h:255
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:274
unsigned int kicked
Definition confbridge.h:285
unsigned int muted
Definition confbridge.h:284
struct confbridge_conference * conference
Definition confbridge.h:275
struct confbridge_user::@98 list
struct ast_channel * chan
Definition confbridge.h:280
struct user_profile u_profile
Definition confbridge.h:277
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:314
struct confbridge_conference * conference
int(* func)(struct confbridge_user *user)
Definition confbridge.h:269
struct post_join_action::@96 list
structure to hold users read from phoneprov_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
#define ast_taskprocessor_push(tps, task_exe, datap)
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).
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
static struct test_val a
#define ast_test_flag(p, flag)
Definition utils.h:64
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_assert(a)
Definition utils.h:779
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition utils.c:2513
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706