2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2007-2008, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
7 * David Vossel <dvossel@digium.com>
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.
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.
22 * \brief Conference Bridge application
24 * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
25 * \author\verbatim David Vossel <dvossel@digium.com> \endverbatim
27 * This is a conference bridge application utilizing the bridging core.
28 * \ingroup applications
31 /*! \li \ref app_confbridge.c uses the configuration file \ref confbridge.conf
32 * \addtogroup configuration_file Configuration Files
36 * \page confbridge.conf confbridge.conf
37 * \verbinclude confbridge.conf.sample
41 <support_level>core</support_level>
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
54 #include "asterisk/cli.h"
55 #include "asterisk/file.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/pbx.h"
59 #include "asterisk/module.h"
60 #include "asterisk/lock.h"
61 #include "asterisk/bridging.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/say.h"
64 #include "asterisk/audiohook.h"
65 #include "asterisk/astobj2.h"
66 #include "confbridge/include/confbridge.h"
67 #include "asterisk/paths.h"
68 #include "asterisk/manager.h"
69 #include "asterisk/test.h"
72 <application name="ConfBridge" language="en_US">
74 Conference bridge application.
77 <parameter name="confno">
78 <para>The conference number</para>
80 <parameter name="bridge_profile">
81 <para>The bridge profile name from confbridge.conf. When left blank, a dynamically built bridge profile created by the CONFBRIDGE dialplan function is searched for on the channel and used. If no dynamic profile is present, the 'default_bridge' profile found in confbridge.conf is used. </para>
82 <para>It is important to note that while user profiles may be unique for each participant, mixing bridge profiles on a single conference is _NOT_ recommended and will produce undefined results.</para>
84 <parameter name="user_profile">
85 <para>The user profile name from confbridge.conf. When left blank, a dynamically built user profile created by the CONFBRIDGE dialplan function is searched for on the channel and used. If no dynamic profile is present, the 'default_user' profile found in confbridge.conf is used.</para>
87 <parameter name="menu">
88 <para>The name of the DTMF menu in confbridge.conf to be applied to this channel. No menu is applied by default if this option is left blank.</para>
92 <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option.</para>
95 <ref type="application">ConfBridge</ref>
96 <ref type="function">CONFBRIDGE</ref>
97 <ref type="function">CONFBRIDGE_INFO</ref>
100 <function name="CONFBRIDGE" language="en_US">
102 Set a custom dynamic bridge and user profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
105 <parameter name="type" required="true">
106 <para>Type refers to which type of profile the option belongs too. Type can be <literal>bridge</literal> or <literal>user</literal>.</para>
108 <parameter name="option" required="true">
109 <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel.</para>
113 <para>---- Example 1 ----</para>
114 <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
115 <para>exten => 1,1,Answer() </para>
116 <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
117 <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
118 <para>exten => 1,n,ConfBridge(1) </para>
119 <para>---- Example 2 ----</para>
120 <para>This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf.</para>
121 <para>exten => 1,1,Answer() </para>
122 <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
123 <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
124 <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
125 <para>exten => 1,n,ConfBridge(1)</para>
128 <function name="CONFBRIDGE_INFO" language="en_US">
130 Get information about a ConfBridge conference.
133 <parameter name="type" required="true">
134 <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
136 <parameter name="conf" required="true">
137 <para>Conf refers to the name of the conference being referenced.</para>
141 <para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para>
144 <manager name="ConfbridgeList" language="en_US">
146 List participants in a conference.
149 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
150 <parameter name="Conference" required="false">
151 <para>Conference number.</para>
155 <para>Lists all users in a particular ConfBridge conference.
156 ConfbridgeList will follow as separate events, followed by a final event called
157 ConfbridgeListComplete.</para>
160 <manager name="ConfbridgeListRooms" language="en_US">
162 List active conferences.
165 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
168 <para>Lists data about all active conferences.
169 ConfbridgeListRooms will follow as separate events, followed by a final event called
170 ConfbridgeListRoomsComplete.</para>
173 <manager name="ConfbridgeMute" language="en_US">
175 Mute a Confbridge user.
178 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
179 <parameter name="Conference" required="true" />
180 <parameter name="Channel" required="true" />
185 <manager name="ConfbridgeUnmute" language="en_US">
187 Unmute a Confbridge user.
190 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
191 <parameter name="Conference" required="true" />
192 <parameter name="Channel" required="true" />
197 <manager name="ConfbridgeKick" language="en_US">
199 Kick a Confbridge user.
202 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
203 <parameter name="Conference" required="true" />
204 <parameter name="Channel" required="true" />
209 <manager name="ConfbridgeLock" language="en_US">
211 Lock a Confbridge conference.
214 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
215 <parameter name="Conference" required="true" />
220 <manager name="ConfbridgeUnlock" language="en_US">
222 Unlock a Confbridge conference.
225 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
226 <parameter name="Conference" required="true" />
231 <manager name="ConfbridgeStartRecord" language="en_US">
233 Start recording a Confbridge conference.
236 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
237 <parameter name="Conference" required="true" />
238 <parameter name="RecordFile" required="false" />
241 <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>
244 <manager name="ConfbridgeStopRecord" language="en_US">
246 Stop recording a Confbridge conference.
249 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
250 <parameter name="Conference" required="true" />
255 <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
257 Set a conference user as the single video source distributed to all other participants.
260 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
261 <parameter name="Conference" required="true" />
262 <parameter name="Channel" required="true" />
271 * \par Playing back a file to a channel in a conference
272 * You might notice in this application that while playing a sound file
273 * to a channel the actual conference bridge lock is not held. This is done so
274 * that other channels are not blocked from interacting with the conference bridge.
275 * Unfortunately because of this it is possible for things to change after the sound file
276 * is done being played. Data must therefore be checked after reacquiring the conference
277 * bridge lock if it is important.
280 static const char app[] = "ConfBridge";
282 /* Number of buckets our conference bridges container can have */
283 #define CONFERENCE_BRIDGE_BUCKETS 53
286 CONF_RECORD_EXIT = 0,
291 /*! \brief Container to hold all conference bridges in progress */
292 static struct ao2_container *conference_bridges;
294 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
295 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number);
296 static int execute_menu_entry(struct conference_bridge *conference_bridge,
297 struct conference_bridge_user *conference_bridge_user,
298 struct ast_bridge_channel *bridge_channel,
299 struct conf_menu_entry *menu_entry,
300 struct conf_menu *menu);
302 /*! \brief Hashing function used for conference bridges container */
303 static int conference_bridge_hash_cb(const void *obj, const int flags)
305 const struct conference_bridge *conference_bridge = obj;
306 return ast_str_case_hash(conference_bridge->name);
309 /*! \brief Comparison function used for conference bridges container */
310 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
312 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
313 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
316 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
319 case CONF_SOUND_HAS_JOINED:
320 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
321 case CONF_SOUND_HAS_LEFT:
322 return S_OR(custom_sounds->hasleft, "conf-hasleft");
323 case CONF_SOUND_KICKED:
324 return S_OR(custom_sounds->kicked, "conf-kicked");
325 case CONF_SOUND_MUTED:
326 return S_OR(custom_sounds->muted, "conf-muted");
327 case CONF_SOUND_UNMUTED:
328 return S_OR(custom_sounds->unmuted, "conf-unmuted");
329 case CONF_SOUND_ONLY_ONE:
330 return S_OR(custom_sounds->onlyone, "conf-onlyone");
331 case CONF_SOUND_THERE_ARE:
332 return S_OR(custom_sounds->thereare, "conf-thereare");
333 case CONF_SOUND_OTHER_IN_PARTY:
334 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
335 case CONF_SOUND_PLACE_IN_CONF:
336 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
337 case CONF_SOUND_WAIT_FOR_LEADER:
338 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
339 case CONF_SOUND_LEADER_HAS_LEFT:
340 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
341 case CONF_SOUND_GET_PIN:
342 return S_OR(custom_sounds->getpin, "conf-getpin");
343 case CONF_SOUND_INVALID_PIN:
344 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
345 case CONF_SOUND_ONLY_PERSON:
346 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
347 case CONF_SOUND_LOCKED:
348 return S_OR(custom_sounds->locked, "conf-locked");
349 case CONF_SOUND_LOCKED_NOW:
350 return S_OR(custom_sounds->lockednow, "conf-lockednow");
351 case CONF_SOUND_UNLOCKED_NOW:
352 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
353 case CONF_SOUND_ERROR_MENU:
354 return S_OR(custom_sounds->errormenu, "conf-errormenu");
355 case CONF_SOUND_JOIN:
356 return S_OR(custom_sounds->join, "confbridge-join");
357 case CONF_SOUND_LEAVE:
358 return S_OR(custom_sounds->leave, "confbridge-leave");
359 case CONF_SOUND_PARTICIPANTS_MUTED:
360 return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
361 case CONF_SOUND_PARTICIPANTS_UNMUTED:
362 return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
368 static struct ast_frame *rec_read(struct ast_channel *ast)
370 return &ast_null_frame;
372 static int rec_write(struct ast_channel *ast, struct ast_frame *f)
376 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
377 static struct ast_channel_tech record_tech = {
378 .type = "ConfBridgeRec",
379 .description = "Conference Bridge Recording Channel",
380 .requester = rec_request,
384 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
386 struct ast_channel *tmp;
387 struct ast_format fmt;
388 const char *conf_name = data;
389 if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
390 "ConfBridgeRecorder/conf-%s-uid-%d",
392 (int) ast_random()))) {
395 ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
396 ast_channel_tech_set(tmp, &record_tech);
397 ast_format_cap_add_all(ast_channel_nativeformats(tmp));
398 ast_format_copy(ast_channel_writeformat(tmp), &fmt);
399 ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
400 ast_format_copy(ast_channel_readformat(tmp), &fmt);
401 ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
405 static void *record_thread(void *obj)
407 struct conference_bridge *conference_bridge = obj;
408 struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
409 struct ast_channel *chan;
410 struct ast_str *filename = ast_str_alloca(PATH_MAX);
412 ast_mutex_lock(&conference_bridge->record_lock);
414 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
415 conference_bridge->record_thread = AST_PTHREADT_NULL;
416 ast_mutex_unlock(&conference_bridge->record_lock);
417 ao2_ref(conference_bridge, -1);
421 /* XXX If we get an EXIT right here, START will essentially be a no-op */
422 while (conference_bridge->record_state != CONF_RECORD_EXIT) {
423 if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
424 ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
428 ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
429 conference_bridge->name,
433 chan = ast_channel_ref(conference_bridge->record_chan);
435 pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
436 ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
438 ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
439 /* STOP has been called. Wait for either a START or an EXIT */
440 ast_cond_wait(&conference_bridge->record_cond, &conference_bridge->record_lock);
442 ast_mutex_unlock(&conference_bridge->record_lock);
443 ao2_ref(conference_bridge, -1);
447 /*! \brief Returns whether or not conference is being recorded.
448 * \param conference_bridge The bridge to check for recording
449 * \retval 1, conference is recording.
450 * \retval 0, conference is NOT recording.
452 static int conf_is_recording(struct conference_bridge *conference_bridge)
454 return conference_bridge->record_state == CONF_RECORD_START;
457 /*! \brief Stop recording a conference bridge
459 * \param conference_bridge The conference bridge on which to stop the recording
463 static int conf_stop_record(struct conference_bridge *conference_bridge)
465 struct ast_channel *chan;
466 if (conference_bridge->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference_bridge)) {
469 conference_bridge->record_state = CONF_RECORD_STOP;
470 chan = ast_channel_ref(conference_bridge->record_chan);
471 ast_bridge_remove(conference_bridge->bridge, chan);
472 ast_queue_frame(chan, &ast_null_frame);
473 chan = ast_channel_unref(chan);
474 ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
481 * \brief Stops the confbridge recording thread.
483 * \note Must be called with the conference_bridge locked
485 static int conf_stop_record_thread(struct conference_bridge *conference_bridge)
487 if (conference_bridge->record_thread == AST_PTHREADT_NULL) {
490 conf_stop_record(conference_bridge);
492 ast_mutex_lock(&conference_bridge->record_lock);
493 conference_bridge->record_state = CONF_RECORD_EXIT;
494 ast_cond_signal(&conference_bridge->record_cond);
495 ast_mutex_unlock(&conference_bridge->record_lock);
497 pthread_join(conference_bridge->record_thread, NULL);
498 conference_bridge->record_thread = AST_PTHREADT_NULL;
500 /* this is the reference given to the channel during the channel alloc */
501 if (conference_bridge->record_chan) {
502 conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan);
508 /*! \brief Start recording the conference
510 * \note conference_bridge must be locked when calling this function
511 * \param conference_bridge The conference bridge to start recording
513 * \rteval non-zero failure
515 static int conf_start_record(struct conference_bridge *conference_bridge)
517 struct ast_format_cap *cap;
518 struct ast_format tmpfmt;
521 if (conference_bridge->record_state != CONF_RECORD_STOP) {
525 if (!pbx_findapp("MixMonitor")) {
526 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
530 if (!(cap = ast_format_cap_alloc_nolock())) {
534 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
536 if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) {
537 cap = ast_format_cap_destroy(cap);
541 cap = ast_format_cap_destroy(cap);
543 conference_bridge->record_state = CONF_RECORD_START;
544 ast_mutex_lock(&conference_bridge->record_lock);
545 ast_cond_signal(&conference_bridge->record_cond);
546 ast_mutex_unlock(&conference_bridge->record_lock);
547 ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
552 /*! \brief Start the recording thread on a conference bridge
554 * \param conference_bridge The conference bridge on which to start the recording thread
558 static int start_conf_record_thread(struct conference_bridge *conference_bridge)
560 ao2_ref(conference_bridge, +1); /* give the record thread a ref */
562 ao2_lock(conference_bridge);
563 conf_start_record(conference_bridge);
564 ao2_unlock(conference_bridge);
566 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
567 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
568 ao2_ref(conference_bridge, -1); /* error so remove ref */
575 static void send_conf_start_event(const char *conf_name)
578 <managerEventInstance>
579 <synopsis>Raised when a conference starts.</synopsis>
581 <parameter name="Conference">
582 <para>The name of the Confbridge conference.</para>
586 <ref type="managerEvent">ConfbridgeEnd</ref>
588 </managerEventInstance>
590 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
593 static void send_conf_end_event(const char *conf_name)
596 <managerEventInstance>
597 <synopsis>Raised when a conference ends.</synopsis>
599 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
602 <ref type="managerEvent">ConfbridgeStart</ref>
603 <ref type="application">ConfBridge</ref>
605 </managerEventInstance>
607 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
610 static void send_join_event(struct ast_channel *chan, const char *conf_name)
613 <managerEventInstance>
614 <synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
616 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
619 <ref type="managerEvent">ConfbridgeLeave</ref>
620 <ref type="application">ConfBridge</ref>
622 </managerEventInstance>
624 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
628 "CallerIDnum: %s\r\n"
629 "CallerIDname: %s\r\n",
630 ast_channel_name(chan),
631 ast_channel_uniqueid(chan),
633 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
634 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
638 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
641 <managerEventInstance>
642 <synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
644 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
647 <ref type="managerEvent">ConfbridgeJoin</ref>
649 </managerEventInstance>
651 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
655 "CallerIDnum: %s\r\n"
656 "CallerIDname: %s\r\n",
657 ast_channel_name(chan),
658 ast_channel_uniqueid(chan),
660 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
661 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
666 * \brief Announce number of users in the conference bridge to the caller
668 * \param conference_bridge Conference bridge to peek at
669 * \param conference_bridge_user Optional Caller
671 * \note if caller is NULL, the announcment will be sent to all participants in the conference.
672 * \return Returns 0 on success, -1 if the user hung up
674 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
676 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
677 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
678 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
680 if (conference_bridge->activeusers <= 1) {
681 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
683 } else if (conference_bridge->activeusers == 2) {
684 if (conference_bridge_user) {
685 /* Eep, there is one other person */
686 if (ast_stream_and_wait(conference_bridge_user->chan,
692 play_sound_file(conference_bridge, only_one);
695 /* Alas multiple others in here */
696 if (conference_bridge_user) {
697 if (ast_stream_and_wait(conference_bridge_user->chan,
702 if (ast_say_number(conference_bridge_user->chan, conference_bridge->activeusers - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) {
705 if (ast_stream_and_wait(conference_bridge_user->chan,
710 } else if (ast_fileexists(there_are, NULL, NULL) && ast_fileexists(other_in_party, NULL, NULL)) {
711 play_sound_file(conference_bridge, there_are);
712 play_sound_number(conference_bridge, conference_bridge->activeusers - 1);
713 play_sound_file(conference_bridge, other_in_party);
720 * \brief Play back an audio file to a channel
722 * \param conference_bridge Conference bridge they are in
723 * \param chan Channel to play audio prompt to
724 * \param file Prompt to play
726 * \return Returns 0 on success, -1 if the user hung up
727 * \note Generally this should be called when the conference is unlocked to avoid blocking
728 * the entire conference while the sound is played. But don't unlock the conference bridge
729 * in the middle of a state transition.
731 static int play_prompt_to_user(struct conference_bridge_user *cbu, const char *filename)
733 return ast_stream_and_wait(cbu->chan, filename, "");
736 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
738 /* Right now, only marked users are automatically set as the single src of video.*/
743 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
745 struct conference_bridge_user *tmp_user = NULL;
746 ao2_lock(conference_bridge);
747 /* see if anyone is already the video src */
748 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
749 if (tmp_user->chan == chan) {
752 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
757 ao2_unlock(conference_bridge);
759 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
761 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
762 /* we joined and are video capable, we override anyone else that may have already been the video feed */
763 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
767 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
769 struct conference_bridge_user *tmp_user = NULL;
771 /* if this isn't a video source, nothing to update */
772 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
776 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
778 /* If in follow talker mode, make sure to restore this mode on the
779 * bridge when a source is removed. It is possible this channel was
780 * only set temporarily as a video source by an AMI or DTMF action. */
781 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
782 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
785 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
786 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
787 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
791 /* Make the next available marked user the video src. */
792 ao2_lock(conference_bridge);
793 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
794 if (tmp_user->chan == chan) {
797 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
798 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
802 ao2_unlock(conference_bridge);
806 * \brief Destroy a conference bridge
808 * \param obj The conference bridge object
810 * \return Returns nothing
812 static void destroy_conference_bridge(void *obj)
814 struct conference_bridge *conference_bridge = obj;
816 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
818 ast_mutex_destroy(&conference_bridge->playback_lock);
820 if (conference_bridge->playback_chan) {
821 struct ast_channel *underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
822 if (underlying_channel) {
823 ast_hangup(underlying_channel);
825 ast_hangup(conference_bridge->playback_chan);
826 conference_bridge->playback_chan = NULL;
829 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
830 if (conference_bridge->bridge) {
831 ast_bridge_destroy(conference_bridge->bridge);
832 conference_bridge->bridge = NULL;
834 conf_bridge_profile_destroy(&conference_bridge->b_profile);
837 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
839 * \param cbu The conference bridge user that is joining
843 static int handle_conf_user_join(struct conference_bridge_user *cbu)
845 conference_event_fn handler;
846 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
847 handler = cbu->conference_bridge->state->join_marked;
848 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
849 handler = cbu->conference_bridge->state->join_waitmarked;
851 handler = cbu->conference_bridge->state->join_unmarked;
854 ast_assert(handler != NULL);
857 conf_invalid_event_fn(cbu);
866 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
868 * \param cbu The conference bridge user that is leaving
872 static int handle_conf_user_leave(struct conference_bridge_user *cbu)
874 conference_event_fn handler;
875 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
876 handler = cbu->conference_bridge->state->leave_marked;
877 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
878 handler = cbu->conference_bridge->state->leave_waitmarked;
880 handler = cbu->conference_bridge->state->leave_unmarked;
883 ast_assert(handler != NULL);
886 /* This should never happen. If it does, though, it is bad. The user will not have been removed
887 * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
888 * Shouldn't happen, though. */
889 conf_invalid_event_fn(cbu);
898 int conf_handle_first_marked_common(struct conference_bridge_user *cbu)
900 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu, conf_get_sound(CONF_SOUND_PLACE_IN_CONF, cbu->b_profile.sounds))) {
906 int conf_handle_inactive_waitmarked(struct conference_bridge_user *cbu)
908 /* Be sure we are muted so we can't talk to anybody else waiting */
909 cbu->features.mute = 1;
910 /* If we have not been quieted play back that they are waiting for the leader */
911 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu,
912 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, cbu->b_profile.sounds))) {
913 /* user hungup while the sound was playing */
916 /* Start music on hold if needed */
917 if (ast_test_flag(&cbu->u_profile, USER_OPT_MUSICONHOLD)) {
918 ast_moh_start(cbu->chan, cbu->u_profile.moh_class, NULL);
919 cbu->playing_moh = 1;
924 int conf_handle_only_unmarked(struct conference_bridge_user *cbu)
926 /* If audio prompts have not been quieted or this prompt quieted play it on out */
927 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
928 if (play_prompt_to_user(cbu,
929 conf_get_sound(CONF_SOUND_ONLY_PERSON, cbu->b_profile.sounds))) {
930 /* user hungup while the sound was playing */
937 int conf_add_post_join_action(struct conference_bridge_user *cbu, int (*func)(struct conference_bridge_user *cbu))
939 struct post_join_action *action;
940 if (!(action = ast_calloc(1, sizeof(*action)))) {
944 AST_LIST_INSERT_TAIL(&cbu->post_join_list, action, list);
949 void conf_handle_first_join(struct conference_bridge *conference_bridge)
951 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
954 void conf_handle_second_active(struct conference_bridge *conference_bridge)
956 /* If we are the second participant we may need to stop music on hold on the first */
957 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->active_list);
959 /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
960 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
961 first_participant->playing_moh = 0;
962 ast_moh_stop(first_participant->chan);
963 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
965 if (!ast_test_flag(&first_participant->u_profile, USER_OPT_STARTMUTED)) {
966 first_participant->features.mute = 0;
970 void conf_ended(struct conference_bridge *conference_bridge)
972 /* Called with a reference to conference_bridge */
973 ao2_unlink(conference_bridges, conference_bridge);
974 send_conf_end_event(conference_bridge->name);
975 conf_stop_record_thread(conference_bridge);
979 * \brief Join a conference bridge
981 * \param name The conference name
982 * \param conference_bridge_user Conference bridge user structure
984 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
986 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
988 struct conference_bridge *conference_bridge = NULL;
989 struct post_join_action *action;
990 struct conference_bridge tmp;
991 int max_members_reached = 0;
993 ast_copy_string(tmp.name, name, sizeof(tmp.name));
995 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
996 ao2_lock(conference_bridges);
998 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
1000 /* Attempt to find an existing conference bridge */
1001 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1003 if (conference_bridge && conference_bridge->b_profile.max_members) {
1004 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->activeusers ? 0 : 1;
1007 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1008 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
1009 ao2_unlock(conference_bridges);
1010 ao2_ref(conference_bridge, -1);
1011 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
1012 ast_stream_and_wait(conference_bridge_user->chan,
1013 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
1018 /* If no conference bridge was found see if we can create one */
1019 if (!conference_bridge) {
1020 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1021 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
1022 ao2_unlock(conference_bridges);
1023 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
1027 /* Setup conference bridge parameters */
1028 conference_bridge->record_thread = AST_PTHREADT_NULL;
1029 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
1030 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
1032 /* Create an actual bridge that will do the audio mixing */
1033 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
1034 ao2_ref(conference_bridge, -1);
1035 conference_bridge = NULL;
1036 ao2_unlock(conference_bridges);
1037 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
1041 /* Set the internal sample rate on the bridge from the bridge profile */
1042 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
1043 /* Set the internal mixing interval on the bridge from the bridge profile */
1044 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
1046 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1047 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
1050 /* Setup lock for playback channel */
1051 ast_mutex_init(&conference_bridge->playback_lock);
1053 /* Setup lock for the record channel */
1054 ast_mutex_init(&conference_bridge->record_lock);
1055 ast_cond_init(&conference_bridge->record_cond, NULL);
1057 /* Link it into the conference bridges container */
1058 ao2_link(conference_bridges, conference_bridge);
1060 /* Set the initial state to EMPTY */
1061 conference_bridge->state = CONF_STATE_EMPTY;
1063 conference_bridge->record_state = CONF_RECORD_STOP;
1064 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
1065 ao2_lock(conference_bridge);
1066 start_conf_record_thread(conference_bridge);
1067 ao2_unlock(conference_bridge);
1070 send_conf_start_event(conference_bridge->name);
1071 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
1074 ao2_unlock(conference_bridges);
1076 /* Setup conference bridge user parameters */
1077 conference_bridge_user->conference_bridge = conference_bridge;
1079 ao2_lock(conference_bridge);
1081 if (handle_conf_user_join(conference_bridge_user)) {
1082 /* Invalid event, nothing was done, so we don't want to process a leave. */
1083 ao2_unlock(conference_bridge);
1084 ao2_ref(conference_bridge, -1);
1088 if (ast_check_hangup(conference_bridge_user->chan)) {
1089 ao2_unlock(conference_bridge);
1090 leave_conference_bridge(conference_bridge, conference_bridge_user);
1094 ao2_unlock(conference_bridge);
1096 /* Announce number of users if need be */
1097 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
1098 if (announce_user_count(conference_bridge, conference_bridge_user)) {
1099 leave_conference_bridge(conference_bridge, conference_bridge_user);
1104 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
1105 (conference_bridge->activeusers > conference_bridge_user->u_profile.announce_user_count_all_after)) {
1106 if (announce_user_count(conference_bridge, NULL)) {
1107 leave_conference_bridge(conference_bridge, conference_bridge_user);
1112 /* Handle post-join actions */
1113 while ((action = AST_LIST_REMOVE_HEAD(&conference_bridge_user->post_join_list, list))) {
1114 action->func(conference_bridge_user);
1118 return conference_bridge;
1122 * \brief Leave a conference bridge
1124 * \param conference_bridge The conference bridge to leave
1125 * \param conference_bridge_user The conference bridge user structure
1128 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1130 ao2_lock(conference_bridge);
1132 handle_conf_user_leave(conference_bridge_user);
1134 /* Done mucking with the conference bridge, huzzah */
1135 ao2_unlock(conference_bridge);
1136 ao2_ref(conference_bridge, -1);
1141 * \brief allocates playback chan on a channel
1142 * \pre expects conference to be locked before calling this function
1144 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
1147 struct ast_format_cap *cap;
1148 struct ast_format tmpfmt;
1150 if (conference_bridge->playback_chan) {
1153 if (!(cap = ast_format_cap_alloc_nolock())) {
1156 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
1157 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
1158 cap = ast_format_cap_destroy(cap);
1161 cap = ast_format_cap_destroy(cap);
1163 ast_channel_internal_bridge_set(conference_bridge->playback_chan, conference_bridge->bridge);
1165 if (ast_call(conference_bridge->playback_chan, "", 0)) {
1166 ast_hangup(conference_bridge->playback_chan);
1167 conference_bridge->playback_chan = NULL;
1171 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
1175 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
1177 struct ast_channel *underlying_channel;
1179 /* Do not waste resources trying to play files that do not exist */
1180 if (!ast_strlen_zero(filename) && !ast_fileexists(filename, NULL, NULL)) {
1181 ast_log(LOG_WARNING, "File %s does not exist in any format\n", !ast_strlen_zero(filename) ? filename : "<unknown>");
1185 ast_mutex_lock(&conference_bridge->playback_lock);
1186 if (!(conference_bridge->playback_chan)) {
1187 if (alloc_playback_chan(conference_bridge)) {
1188 ast_mutex_unlock(&conference_bridge->playback_lock);
1191 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1193 /* Channel was already available so we just need to add it back into the bridge */
1194 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1195 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0);
1198 /* The channel is all under our control, in goes the prompt */
1199 if (!ast_strlen_zero(filename)) {
1200 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
1201 } else if (say_number >= 0) {
1202 ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL);
1205 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge);
1206 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
1208 ast_mutex_unlock(&conference_bridge->playback_lock);
1213 int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
1215 return play_sound_helper(conference_bridge, filename, -1);
1219 * \brief Play number into the conference bridge
1221 * \param conference_bridge The conference bridge to say the number into
1222 * \param say_number number to say
1225 * \retval -1 failure
1227 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
1229 return play_sound_helper(conference_bridge, NULL, say_number);
1232 static void conf_handle_talker_destructor(void *pvt_data)
1237 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
1239 char *conf_name = pvt_data;
1242 switch (bridge_channel->state) {
1243 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
1246 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
1250 return; /* uhh this shouldn't happen, but bail if it does. */
1253 /* notify AMI someone is has either started or stopped talking */
1255 <managerEventInstance>
1256 <synopsis>Raised when a conference participant has started or stopped talking.</synopsis>
1258 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
1259 <parameter name="TalkingStatus">
1266 </managerEventInstance>
1268 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
1271 "Conference: %s\r\n"
1272 "TalkingStatus: %s\r\n",
1273 ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
1276 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
1278 char pin_guess[MAX_PIN+1] = { 0, };
1279 const char *pin = conference_bridge_user->u_profile.pin;
1280 char *tmp = pin_guess;
1282 unsigned int len = MAX_PIN ;
1284 /* give them three tries to get the pin right */
1285 for (i = 0; i < 3; i++) {
1286 if (ast_app_getdata(chan,
1287 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
1288 tmp, len, 0) >= 0) {
1289 if (!strcasecmp(pin, pin_guess)) {
1293 ast_streamfile(chan,
1294 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
1295 ast_channel_language(chan));
1296 res = ast_waitstream(chan, AST_DIGIT_ANY);
1298 /* Account for digit already read during ivalid pin playback
1299 * resetting pin buf. */
1301 pin_guess[1] = '\0';
1302 tmp = pin_guess + 1;
1305 /* reset pin buf as empty buffer. */
1313 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
1315 char destdir[PATH_MAX];
1319 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1321 if (ast_mkdir(destdir, 0777) != 0) {
1322 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1325 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1326 "%s/confbridge-name-%s-%s", destdir,
1327 conf_name, ast_channel_uniqueid(user->chan));
1329 res = ast_play_and_record(user->chan,
1331 user->name_rec_location,
1336 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1341 user->name_rec_location[0] = '\0';
1347 /*! \brief The ConfBridge application */
1348 static int confbridge_exec(struct ast_channel *chan, const char *data)
1350 int res = 0, volume_adjustments[2];
1353 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
1354 const char *u_profile_name = DEFAULT_USER_PROFILE;
1355 struct conference_bridge *conference_bridge = NULL;
1356 struct conference_bridge_user conference_bridge_user = {
1358 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1359 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1360 .tech_args.drop_silence = 0,
1362 AST_DECLARE_APP_ARGS(args,
1363 AST_APP_ARG(conf_name);
1364 AST_APP_ARG(b_profile_name);
1365 AST_APP_ARG(u_profile_name);
1366 AST_APP_ARG(menu_name);
1368 ast_bridge_features_init(&conference_bridge_user.features);
1370 if (ast_channel_state(chan) != AST_STATE_UP) {
1374 if (ast_strlen_zero(data)) {
1375 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1376 res = -1; /* invalid PIN */
1377 goto confbridge_cleanup;
1380 /* We need to make a copy of the input string if we are going to modify it! */
1381 parse = ast_strdupa(data);
1383 AST_STANDARD_APP_ARGS(args, parse);
1385 /* bridge profile name */
1386 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1387 b_profile_name = args.b_profile_name;
1389 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
1390 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
1392 goto confbridge_cleanup;
1395 /* user profile name */
1396 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1397 u_profile_name = args.u_profile_name;
1400 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
1401 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
1403 goto confbridge_cleanup;
1406 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
1408 /* ask for a PIN immediately after finding user profile. This has to be
1409 * prompted for requardless of quiet setting. */
1410 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
1411 if (conf_get_pin(chan, &conference_bridge_user)) {
1412 res = -1; /* invalid PIN */
1413 goto confbridge_cleanup;
1417 /* See if we need them to record a intro name */
1418 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
1419 conf_rec_name(&conference_bridge_user, args.conf_name);
1423 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
1424 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
1425 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
1426 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
1428 res = -1; /* invalid PIN */
1429 goto confbridge_cleanup;
1433 /* Set if DTMF should pass through for this user or not */
1434 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
1435 conference_bridge_user.features.dtmf_passthrough = 1;
1438 /* Set dsp threshold values if present */
1439 if (conference_bridge_user.u_profile.talking_threshold) {
1440 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
1442 if (conference_bridge_user.u_profile.silence_threshold) {
1443 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
1446 /* Set a talker indicate call back if talking detection is requested */
1447 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
1448 char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
1450 res = -1; /* invalid PIN */
1451 goto confbridge_cleanup;
1453 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
1454 conf_handle_talker_cb,
1455 conf_handle_talker_destructor,
1459 /* Look for a conference bridge matching the provided name */
1460 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
1461 res = -1; /* invalid PIN */
1462 goto confbridge_cleanup;
1465 /* Keep a copy of volume adjustments so we can restore them later if need be */
1466 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1467 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1469 /* If the caller should be joined already muted, make it so */
1470 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
1471 conference_bridge_user.features.mute = 1;
1474 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
1475 conference_bridge_user.tech_args.drop_silence = 1;
1478 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
1480 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1482 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1486 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
1488 /* Reduce background noise from each participant */
1489 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1490 ast_free(mod_speex);
1491 ast_func_write(chan, "DENOISE(rx)", "on");
1495 /* if this user has a intro, play it before entering */
1496 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1497 ast_autoservice_start(chan);
1498 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1499 play_sound_file(conference_bridge,
1500 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
1501 ast_autoservice_stop(chan);
1504 /* Play the Join sound to both the conference and the user entering. */
1506 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
1507 if (conference_bridge_user.playing_moh) {
1510 ast_stream_and_wait(chan, join_sound, "");
1511 ast_autoservice_start(chan);
1512 play_sound_file(conference_bridge, join_sound);
1513 ast_autoservice_stop(chan);
1514 if (conference_bridge_user.playing_moh) {
1515 ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
1519 /* See if we need to automatically set this user as a video source or not */
1520 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
1522 /* Join our conference bridge for real */
1523 send_join_event(conference_bridge_user.chan, conference_bridge->name);
1524 ast_bridge_join(conference_bridge->bridge,
1527 &conference_bridge_user.features,
1528 &conference_bridge_user.tech_args);
1529 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
1531 /* if we're shutting down, don't attempt to do further processing */
1532 if (ast_shutting_down()) {
1533 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1534 conference_bridge = NULL;
1535 goto confbridge_cleanup;
1538 /* If this user was a video source, we need to clean up and possibly pick a new source. */
1539 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
1541 /* if this user has a intro, play it when leaving */
1542 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1543 ast_autoservice_start(chan);
1544 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1545 play_sound_file(conference_bridge,
1546 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
1547 ast_autoservice_stop(chan);
1550 /* play the leave sound */
1552 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
1553 ast_autoservice_start(chan);
1554 play_sound_file(conference_bridge, leave_sound);
1555 ast_autoservice_stop(chan);
1558 /* Easy as pie, depart this channel from the conference bridge */
1559 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1560 conference_bridge = NULL;
1562 /* If the user was kicked from the conference play back the audio prompt for it */
1563 if (!quiet && conference_bridge_user.kicked) {
1564 res = ast_stream_and_wait(chan,
1565 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
1569 /* Restore volume adjustments to previous values in case they were changed */
1570 if (volume_adjustments[0]) {
1571 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1573 if (volume_adjustments[1]) {
1574 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1577 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1578 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
1582 ast_bridge_features_cleanup(&conference_bridge_user.features);
1583 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
1587 static int action_toggle_mute(struct conference_bridge *conference_bridge,
1588 struct conference_bridge_user *conference_bridge_user,
1589 struct ast_channel *chan)
1591 /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
1592 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
1593 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
1594 ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(chan), conference_bridge_user->features.mute ? "muted" : "unmuted", conference_bridge_user->b_profile.name, ast_channel_name(chan));
1596 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
1597 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
1598 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
1602 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1604 struct conference_bridge_user *participant = NULL;
1605 const char *sound_to_play;
1607 ao2_lock(conference_bridge);
1609 /* If already muted, then unmute */
1610 conference_bridge->muted = conference_bridge->muted ? 0 : 1;
1611 sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
1612 conference_bridge_user->b_profile.sounds);
1614 AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) {
1615 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
1616 participant->features.mute = conference_bridge->muted;
1620 ao2_unlock(conference_bridge);
1622 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
1623 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
1625 /* Announce to the group that all participants are muted */
1626 ast_autoservice_start(conference_bridge_user->chan);
1627 play_sound_helper(conference_bridge, sound_to_play, 0);
1628 ast_autoservice_stop(conference_bridge_user->chan);
1633 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1635 char *file_copy = ast_strdupa(playback_file);
1638 while ((file = strsep(&file_copy, "&"))) {
1639 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1640 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1647 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
1648 struct conference_bridge_user *conference_bridge_user,
1649 struct ast_bridge_channel *bridge_channel,
1650 struct conf_menu *menu,
1651 const char *playback_file,
1652 const char *cur_dtmf,
1657 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1658 struct conf_menu_entry new_menu_entry = { { 0, }, };
1659 char *file_copy = ast_strdupa(playback_file);
1662 while ((file = strsep(&file_copy, "&"))) {
1663 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
1664 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1668 /* now wait for more digits. */
1669 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1670 /* streaming finished and no DTMF was entered */
1672 } else if (digit == -1) {
1676 break; /* dtmf was entered */
1680 /* streaming finished on all files and no DTMF was entered */
1683 ast_stopstream(bridge_channel->chan);
1685 /* If we get here, then DTMF has been entered, This means no
1686 * additional prompts should be played for this menu entry */
1689 /* If a digit was pressed during the payback, update
1690 * the dtmf string and look for a new menu entry in the
1692 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1693 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1694 dtmf[i] = cur_dtmf[i];
1696 dtmf[i] = (char) digit;
1702 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1703 * If this is the case, no new DTMF sequence should be looked for. */
1708 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1709 execute_menu_entry(conference_bridge,
1710 conference_bridge_user,
1712 &new_menu_entry, menu);
1713 conf_menu_entry_destroy(&new_menu_entry);
1718 static int action_kick_last(struct conference_bridge *conference_bridge,
1719 struct ast_bridge_channel *bridge_channel,
1720 struct conference_bridge_user *conference_bridge_user)
1722 struct conference_bridge_user *last_participant = NULL;
1723 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1726 ast_stream_and_wait(bridge_channel->chan,
1727 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1729 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1730 ast_channel_name(bridge_channel->chan),
1731 conference_bridge->name);
1735 ao2_lock(conference_bridge);
1736 if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user)
1737 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
1738 ao2_unlock(conference_bridge);
1739 ast_stream_and_wait(bridge_channel->chan,
1740 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1742 } else if (last_participant) {
1743 last_participant->kicked = 1;
1744 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
1745 ao2_unlock(conference_bridge);
1750 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1752 struct ast_pbx_args args;
1753 struct ast_pbx *pbx;
1759 memset(&args, 0, sizeof(args));
1760 args.no_hangup_chan = 1;
1762 ast_channel_lock(bridge_channel->chan);
1765 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
1766 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
1767 priority = ast_channel_priority(bridge_channel->chan);
1768 pbx = ast_channel_pbx(bridge_channel->chan);
1769 ast_channel_pbx_set(bridge_channel->chan, NULL);
1772 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
1773 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
1774 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
1776 ast_channel_unlock(bridge_channel->chan);
1779 res = ast_pbx_run_args(bridge_channel->chan, &args);
1782 ast_channel_lock(bridge_channel->chan);
1784 ast_channel_exten_set(bridge_channel->chan, exten);
1785 ast_channel_context_set(bridge_channel->chan, context);
1786 ast_channel_priority_set(bridge_channel->chan, priority);
1787 ast_channel_pbx_set(bridge_channel->chan, pbx);
1789 ast_channel_unlock(bridge_channel->chan);
1794 static int execute_menu_entry(struct conference_bridge *conference_bridge,
1795 struct conference_bridge_user *conference_bridge_user,
1796 struct ast_bridge_channel *bridge_channel,
1797 struct conf_menu_entry *menu_entry,
1798 struct conf_menu *menu)
1800 struct conf_menu_action *menu_action;
1801 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1802 int stop_prompts = 0;
1805 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1806 switch (menu_action->id) {
1807 case MENU_ACTION_TOGGLE_MUTE:
1808 res |= action_toggle_mute(conference_bridge,
1809 conference_bridge_user,
1810 bridge_channel->chan);
1812 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1816 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
1818 case MENU_ACTION_PARTICIPANT_COUNT:
1819 announce_user_count(conference_bridge, conference_bridge_user);
1821 case MENU_ACTION_PLAYBACK:
1822 if (!stop_prompts) {
1823 res |= action_playback(bridge_channel, menu_action->data.playback_file);
1826 case MENU_ACTION_RESET_LISTENING:
1827 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
1829 case MENU_ACTION_RESET_TALKING:
1830 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
1832 case MENU_ACTION_INCREASE_LISTENING:
1833 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1834 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
1836 case MENU_ACTION_DECREASE_LISTENING:
1837 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1838 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
1840 case MENU_ACTION_INCREASE_TALKING:
1841 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1842 AST_AUDIOHOOK_DIRECTION_READ, 1);
1844 case MENU_ACTION_DECREASE_TALKING:
1845 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1846 AST_AUDIOHOOK_DIRECTION_READ, -1);
1848 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1849 if (!(stop_prompts)) {
1850 res |= action_playback_and_continue(conference_bridge,
1851 conference_bridge_user,
1854 menu_action->data.playback_file,
1859 case MENU_ACTION_DIALPLAN_EXEC:
1860 res |= action_dialplan_exec(bridge_channel, menu_action);
1862 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1866 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
1867 res |= ast_stream_and_wait(bridge_channel->chan,
1868 (conference_bridge->locked ?
1869 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
1870 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
1874 case MENU_ACTION_ADMIN_KICK_LAST:
1875 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
1877 case MENU_ACTION_LEAVE:
1878 ao2_lock(conference_bridge);
1879 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
1880 ao2_unlock(conference_bridge);
1882 case MENU_ACTION_NOOP:
1884 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1885 ao2_lock(conference_bridge);
1886 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
1887 ao2_unlock(conference_bridge);
1889 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1890 handle_video_on_exit(conference_bridge, bridge_channel->chan);
1897 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
1898 struct conference_bridge_user *conference_bridge_user,
1899 struct conf_menu_entry *menu_entry,
1900 struct conf_menu *menu)
1902 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
1904 /* See if music on hold is playing */
1905 ao2_lock(conference_bridge);
1906 if (conference_bridge_user->playing_moh) {
1907 /* MOH is going, let's stop it */
1908 ast_moh_stop(bridge_channel->chan);
1910 ao2_unlock(conference_bridge);
1912 /* execute the list of actions associated with this menu entry */
1913 execute_menu_entry(conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
1915 /* See if music on hold needs to be started back up again */
1916 ao2_lock(conference_bridge);
1917 if (conference_bridge_user->playing_moh) {
1918 ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL);
1920 ao2_unlock(conference_bridge);
1925 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
1928 struct conference_bridge *bridge = NULL;
1930 int wordlen = strlen(word);
1931 struct ao2_iterator i;
1933 i = ao2_iterator_init(conference_bridges, 0);
1934 while ((bridge = ao2_iterator_next(&i))) {
1935 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
1936 res = ast_strdup(bridge->name);
1937 ao2_ref(bridge, -1);
1940 ao2_ref(bridge, -1);
1942 ao2_iterator_destroy(&i);
1947 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1949 struct conference_bridge *bridge = NULL;
1950 struct conference_bridge tmp;
1951 struct conference_bridge_user *participant = NULL;
1955 e->command = "confbridge kick";
1957 "Usage: confbridge kick <conference> <channel>\n"
1958 " Kicks a channel out of the conference bridge.\n";
1962 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
1966 return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
1973 return CLI_SHOWUSAGE;
1976 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
1977 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1979 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
1983 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
1984 if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
1989 ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
1990 participant->kicked = 1;
1991 ast_bridge_remove(bridge->bridge, participant->chan);
1994 ao2_ref(bridge, -1);
1998 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2000 struct ao2_iterator i;
2001 struct conference_bridge *bridge = NULL;
2002 struct conference_bridge tmp;
2003 struct conference_bridge_user *participant = NULL;
2007 e->command = "confbridge list";
2009 "Usage: confbridge list [<name>]\n"
2010 " Lists all currently active conference bridges.\n";
2014 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2020 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
2021 ast_cli(a->fd, "================================ ====== ====== ========\n");
2022 i = ao2_iterator_init(conference_bridges, 0);
2023 while ((bridge = ao2_iterator_next(&i))) {
2024 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
2025 ao2_ref(bridge, -1);
2027 ao2_iterator_destroy(&i);
2032 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2033 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2035 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2038 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
2039 ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
2041 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2042 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
2043 ast_cli(a->fd, "%-17s", participant->u_profile.name);
2044 ast_cli(a->fd, "%-17s", participant->b_profile.name);
2045 ast_cli(a->fd, "%-17s", participant->menu_name);
2046 ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
2047 ast_cli(a->fd, "\n");
2050 ao2_ref(bridge, -1);
2054 return CLI_SHOWUSAGE;
2058 * \brief finds a conference by name and locks/unlocks.
2061 * \retval -1 conference not found
2063 static int generic_lock_unlock_helper(int lock, const char *conference)
2065 struct conference_bridge *bridge = NULL;
2066 struct conference_bridge tmp;
2069 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2070 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2075 bridge->locked = lock;
2076 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
2078 ao2_ref(bridge, -1);
2084 * \brief finds a conference user by channel name and mutes/unmutes them.
2087 * \retval -1 conference not found
2088 * \retval -2 user not found
2090 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
2092 struct conference_bridge *bridge = NULL;
2093 struct conference_bridge tmp;
2094 struct conference_bridge_user *participant = NULL;
2096 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2097 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2102 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2103 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
2108 participant->features.mute = mute;
2109 ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(participant->chan), participant->features.mute ? "muted" : "unmuted", bridge->b_profile.name, ast_channel_name(participant->chan));
2114 ao2_ref(bridge, -1);
2119 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2121 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2124 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2126 } else if (res == -2) {
2127 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2130 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2134 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2138 e->command = "confbridge mute";
2140 "Usage: confbridge mute <conference> <channel>\n";
2144 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2149 return CLI_SHOWUSAGE;
2152 cli_mute_unmute_helper(1, a);
2157 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2161 e->command = "confbridge unmute";
2163 "Usage: confbridge unmute <conference> <channel>\n";
2167 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2172 return CLI_SHOWUSAGE;
2175 cli_mute_unmute_helper(0, a);
2180 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2184 e->command = "confbridge lock";
2186 "Usage: confbridge lock <conference>\n";
2190 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2195 return CLI_SHOWUSAGE;
2197 if (generic_lock_unlock_helper(1, a->argv[2])) {
2198 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2200 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2205 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2209 e->command = "confbridge unlock";
2211 "Usage: confbridge unlock <conference>\n";
2215 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2220 return CLI_SHOWUSAGE;
2222 if (generic_lock_unlock_helper(0, a->argv[2])) {
2223 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2225 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2230 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2232 const char *rec_file = NULL;
2233 struct conference_bridge *bridge = NULL;
2234 struct conference_bridge tmp;
2238 e->command = "confbridge record start";
2240 "Usage: confbridge record start <conference> <file>\n"
2241 " <file> is optional, Otherwise the bridge profile\n"
2242 " record file will be used. If the bridge profile\n"
2243 " has no record file specified, a file will automatically\n"
2244 " be generated in the monitor directory\n";
2248 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2253 return CLI_SHOWUSAGE;
2256 rec_file = a->argv[4];
2259 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2260 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2262 ast_cli(a->fd, "Conference not found.\n");
2266 if (conf_is_recording(bridge)) {
2267 ast_cli(a->fd, "Conference is already being recorded.\n");
2269 ao2_ref(bridge, -1);
2272 if (!ast_strlen_zero(rec_file)) {
2273 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2276 if (start_conf_record_thread(bridge)) {
2277 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2279 ao2_ref(bridge, -1);
2284 ast_cli(a->fd, "Recording started\n");
2285 ao2_ref(bridge, -1);
2289 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2291 struct conference_bridge *bridge = NULL;
2292 struct conference_bridge tmp;
2297 e->command = "confbridge record stop";
2299 "Usage: confbridge record stop <conference>\n";
2303 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2308 return CLI_SHOWUSAGE;
2311 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2312 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2314 ast_cli(a->fd, "Conference not found.\n");
2318 ret = conf_stop_record(bridge);
2320 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
2321 ao2_ref(bridge, -1);
2325 static struct ast_cli_entry cli_confbridge[] = {
2326 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2327 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2328 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2329 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
2330 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2331 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2332 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2333 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2335 static struct ast_custom_function confbridge_function = {
2336 .name = "CONFBRIDGE",
2337 .write = func_confbridge_helper,
2340 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2341 static struct ast_custom_function confbridge_info_function = {
2342 .name = "CONFBRIDGE_INFO",
2343 .read = func_confbridge_info,
2346 static int action_confbridgelist(struct mansession *s, const struct message *m)
2348 const char *actionid = astman_get_header(m, "ActionID");
2349 const char *conference = astman_get_header(m, "Conference");
2350 struct conference_bridge_user *participant = NULL;
2351 struct conference_bridge *bridge = NULL;
2352 struct conference_bridge tmp;
2353 char id_text[80] = "";
2356 if (!ast_strlen_zero(actionid)) {
2357 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2359 if (ast_strlen_zero(conference)) {
2360 astman_send_error(s, m, "No Conference name provided.");
2363 if (!ao2_container_count(conference_bridges)) {
2364 astman_send_error(s, m, "No active conferences.");
2367 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2368 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2370 astman_send_error(s, m, "No Conference by that name found.");
2374 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2377 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2380 "Event: ConfbridgeList\r\n"
2382 "Conference: %s\r\n"
2383 "CallerIDNum: %s\r\n"
2384 "CallerIDName: %s\r\n"
2387 "MarkedUser: %s\r\n"
2391 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
2392 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
2393 ast_channel_name(participant->chan),
2394 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2395 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
2398 ao2_ref(bridge, -1);
2401 "Event: ConfbridgeListComplete\r\n"
2402 "EventList: Complete\r\n"
2405 "\r\n", total, id_text);
2410 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2412 const char *actionid = astman_get_header(m, "ActionID");
2413 struct conference_bridge *bridge = NULL;
2414 struct ao2_iterator i;
2415 char id_text[512] = "";
2418 if (!ast_strlen_zero(actionid)) {
2419 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2422 if (!ao2_container_count(conference_bridges)) {
2423 astman_send_error(s, m, "No active conferences.");
2427 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2429 /* Traverse the conference list */
2430 i = ao2_iterator_init(conference_bridges, 0);
2431 while ((bridge = ao2_iterator_next(&i))) {
2436 "Event: ConfbridgeListRooms\r\n"
2438 "Conference: %s\r\n"
2445 bridge->activeusers,
2446 bridge->markedusers,
2447 bridge->locked ? "Yes" : "No");
2450 ao2_ref(bridge, -1);
2452 ao2_iterator_destroy(&i);
2454 /* Send final confirmation */
2456 "Event: ConfbridgeListRoomsComplete\r\n"
2457 "EventList: Complete\r\n"
2460 "\r\n", totalitems, id_text);
2464 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2466 const char *conference = astman_get_header(m, "Conference");
2467 const char *channel = astman_get_header(m, "Channel");
2470 if (ast_strlen_zero(conference)) {
2471 astman_send_error(s, m, "No Conference name provided.");
2474 if (ast_strlen_zero(channel)) {
2475 astman_send_error(s, m, "No channel name provided.");
2478 if (!ao2_container_count(conference_bridges)) {
2479 astman_send_error(s, m, "No active conferences.");
2483 res = generic_mute_unmute_helper(mute, conference, channel);
2486 astman_send_error(s, m, "No Conference by that name found.");
2488 } else if (res == -2) {
2489 astman_send_error(s, m, "No Channel by that name found in Conference.");
2493 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2497 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2499 return action_mute_unmute_helper(s, m, 0);
2501 static int action_confbridgemute(struct mansession *s, const struct message *m)
2503 return action_mute_unmute_helper(s, m, 1);
2506 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2508 const char *conference = astman_get_header(m, "Conference");
2511 if (ast_strlen_zero(conference)) {
2512 astman_send_error(s, m, "No Conference name provided.");
2515 if (!ao2_container_count(conference_bridges)) {
2516 astman_send_error(s, m, "No active conferences.");
2519 if ((res = generic_lock_unlock_helper(lock, conference))) {
2520 astman_send_error(s, m, "No Conference by that name found.");
2523 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2526 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2528 return action_lock_unlock_helper(s, m, 0);
2530 static int action_confbridgelock(struct mansession *s, const struct message *m)
2532 return action_lock_unlock_helper(s, m, 1);
2535 static int action_confbridgekick(struct mansession *s, const struct message *m)
2537 const char *conference = astman_get_header(m, "Conference");
2538 const char *channel = astman_get_header(m, "Channel");
2539 struct conference_bridge_user *participant = NULL;
2540 struct conference_bridge *bridge = NULL;
2541 struct conference_bridge tmp;
2544 if (ast_strlen_zero(conference)) {
2545 astman_send_error(s, m, "No Conference name provided.");
2548 if (!ao2_container_count(conference_bridges)) {
2549 astman_send_error(s, m, "No active conferences.");
2552 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2553 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2555 astman_send_error(s, m, "No Conference by that name found.");
2560 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2561 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
2562 participant->kicked = 1;
2563 ast_bridge_remove(bridge->bridge, participant->chan);
2569 ao2_ref(bridge, -1);
2572 astman_send_ack(s, m, "User kicked");
2574 astman_send_error(s, m, "No Channel by that name found in Conference.");
2579 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2581 const char *conference = astman_get_header(m, "Conference");
2582 const char *recordfile = astman_get_header(m, "RecordFile");
2583 struct conference_bridge *bridge = NULL;
2584 struct conference_bridge tmp;
2586 if (ast_strlen_zero(conference)) {
2587 astman_send_error(s, m, "No Conference name provided.");
2590 if (!ao2_container_count(conference_bridges)) {
2591 astman_send_error(s, m, "No active conferences.");
2595 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2596 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2598 astman_send_error(s, m, "No Conference by that name found.");
2603 if (conf_is_recording(bridge)) {
2604 astman_send_error(s, m, "Conference is already being recorded.");
2606 ao2_ref(bridge, -1);
2610 if (!ast_strlen_zero(recordfile)) {
2611 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2614 if (start_conf_record_thread(bridge)) {
2615 astman_send_error(s, m, "Internal error starting conference recording.");
2617 ao2_ref(bridge, -1);
2622 ao2_ref(bridge, -1);
2623 astman_send_ack(s, m, "Conference Recording Started.");
2626 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2628 const char *conference = astman_get_header(m, "Conference");
2629 struct conference_bridge *bridge = NULL;
2630 struct conference_bridge tmp;
2632 if (ast_strlen_zero(conference)) {
2633 astman_send_error(s, m, "No Conference name provided.");
2636 if (!ao2_container_count(conference_bridges)) {
2637 astman_send_error(s, m, "No active conferences.");
2641 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2642 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2644 astman_send_error(s, m, "No Conference by that name found.");
2649 if (conf_stop_record(bridge)) {
2651 astman_send_error(s, m, "Internal error while stopping recording.");
2652 ao2_ref(bridge, -1);
2657 ao2_ref(bridge, -1);
2658 astman_send_ack(s, m, "Conference Recording Stopped.");
2662 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2664 const char *conference = astman_get_header(m, "Conference");
2665 const char *channel = astman_get_header(m, "Channel");
2666 struct conference_bridge_user *participant = NULL;
2667 struct conference_bridge *bridge = NULL;
2668 struct conference_bridge tmp;
2670 if (ast_strlen_zero(conference)) {
2671 astman_send_error(s, m, "No Conference name provided.");
2674 if (ast_strlen_zero(channel)) {
2675 astman_send_error(s, m, "No channel name provided.");
2678 if (!ao2_container_count(conference_bridges)) {
2679 astman_send_error(s, m, "No active conferences.");
2683 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2684 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2686 astman_send_error(s, m, "No Conference by that name found.");
2690 /* find channel and set as video src. */
2692 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2693 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
2694 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2699 ao2_ref(bridge, -1);
2701 /* do not access participant after bridge unlock. We are just
2702 * using this check to see if it was found or not */
2704 astman_send_error(s, m, "No channel by that name found in conference.");
2707 astman_send_ack(s, m, "Conference single video source set.");
2711 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2714 struct conference_bridge *bridge = NULL;
2715 struct conference_bridge_user *participant = NULL;
2716 struct conference_bridge tmp;
2718 AST_DECLARE_APP_ARGS(args,
2720 AST_APP_ARG(confno);
2723 /* parse all the required arguments and make sure they exist. */
2724 if (ast_strlen_zero(data)) {
2727 parse = ast_strdupa(data);
2728 AST_STANDARD_APP_ARGS(args, parse);
2729 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
2732 if (!ao2_container_count(conference_bridges)) {
2733 snprintf(buf, len, "0");
2736 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
2737 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2739 snprintf(buf, len, "0");
2743 /* get the correct count for the type requested */
2745 if (!strncasecmp(args.type, "parties", 7)) {
2746 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2749 } else if (!strncasecmp(args.type, "admins", 6)) {
2750 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2751 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
2755 } else if (!strncasecmp(args.type, "marked", 6)) {
2756 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2757 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
2761 } else if (!strncasecmp(args.type, "locked", 6)) {
2762 count = bridge->locked;
2764 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
2765 "parties, admins, marked, or locked.\n", args.type);
2767 snprintf(buf, len, "%d", count);
2769 ao2_ref(bridge, -1);
2773 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2775 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
2776 conference_bridge->activeusers++;
2779 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2781 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
2782 conference_bridge->activeusers++;
2783 conference_bridge->markedusers++;
2786 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2788 AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
2789 conference_bridge->waitingusers++;
2792 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2794 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
2795 conference_bridge->activeusers--;
2798 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2800 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
2801 conference_bridge->activeusers--;
2802 conference_bridge->markedusers--;
2805 void conf_mute_only_active(struct conference_bridge *conference_bridge)
2807 struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
2809 /* Turn on MOH/mute if the single participant is set up for it */
2810 if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
2811 only_participant->features.mute = 1;
2812 if (!ast_channel_internal_bridge(only_participant->chan) || !ast_bridge_suspend(conference_bridge->bridge, only_participant->chan)) {
2813 ast_moh_start(only_participant->chan, only_participant->u_profile.moh_class, NULL);
2814 only_participant->playing_moh = 1;
2815 if (ast_channel_internal_bridge(only_participant->chan)) {
2816 ast_bridge_unsuspend(conference_bridge->bridge, only_participant->chan);
2822 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2824 AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
2825 conference_bridge->waitingusers--;
2828 /*! \brief Called when module is being unloaded */
2829 static int unload_module(void)
2831 int res = ast_unregister_application(app);
2833 ast_custom_function_unregister(&confbridge_function);
2834 ast_custom_function_unregister(&confbridge_info_function);
2836 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2838 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
2839 ao2_ref(conference_bridges, -1);
2841 conf_destroy_config();
2843 ast_channel_unregister(&record_tech);
2844 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
2846 res |= ast_manager_unregister("ConfbridgeList");
2847 res |= ast_manager_unregister("ConfbridgeListRooms");
2848 res |= ast_manager_unregister("ConfbridgeMute");
2849 res |= ast_manager_unregister("ConfbridgeUnmute");
2850 res |= ast_manager_unregister("ConfbridgeKick");
2851 res |= ast_manager_unregister("ConfbridgeUnlock");
2852 res |= ast_manager_unregister("ConfbridgeLock");
2853 res |= ast_manager_unregister("ConfbridgeStartRecord");
2854 res |= ast_manager_unregister("ConfbridgeStopRecord");
2860 * \brief Load the module
2862 * Module loading including tests for configuration or dependencies.
2863 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2864 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2865 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2866 * configuration file or other non-critical problem return
2867 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2869 static int load_module(void)
2873 if (conf_load_config(0)) {
2874 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2875 return AST_MODULE_LOAD_DECLINE;
2877 if ((ast_custom_function_register(&confbridge_function))) {
2878 return AST_MODULE_LOAD_FAILURE;
2880 if ((ast_custom_function_register(&confbridge_info_function))) {
2881 return AST_MODULE_LOAD_FAILURE;
2883 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
2884 return AST_MODULE_LOAD_FAILURE;
2886 ast_format_cap_add_all(record_tech.capabilities);
2887 if (ast_channel_register(&record_tech)) {
2888 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
2889 return AST_MODULE_LOAD_FAILURE;
2891 /* Create a container to hold the conference bridges */
2892 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
2893 return AST_MODULE_LOAD_FAILURE;
2895 if (ast_register_application_xml(app, confbridge_exec)) {
2896 ao2_ref(conference_bridges, -1);
2897 return AST_MODULE_LOAD_FAILURE;
2900 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2901 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
2902 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
2903 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
2904 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
2905 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
2906 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
2907 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
2908 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
2909 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
2910 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
2912 return AST_MODULE_LOAD_FAILURE;
2915 return AST_MODULE_LOAD_SUCCESS;
2918 static int reload(void)
2920 return conf_load_config(1);
2923 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
2924 .load = load_module,
2925 .unload = unload_module,
2927 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,