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="conference" required="true">
78 <para>Name of the conference bridge. You are not limited to just
81 <parameter name="bridge_profile">
82 <para>The bridge profile name from confbridge.conf. When left blank,
83 a dynamically built bridge profile created by the CONFBRIDGE dialplan
84 function is searched for on the channel and used. If no dynamic
85 profile is present, the 'default_bridge' profile found in
86 confbridge.conf is used. </para>
87 <para>It is important to note that while user profiles may be unique
88 for each participant, mixing bridge profiles on a single conference
89 is _NOT_ recommended and will produce undefined results.</para>
91 <parameter name="user_profile">
92 <para>The user profile name from confbridge.conf. When left blank,
93 a dynamically built user profile created by the CONFBRIDGE dialplan
94 function is searched for on the channel and used. If no dynamic
95 profile is present, the 'default_user' profile found in
96 confbridge.conf is used.</para>
98 <parameter name="menu">
99 <para>The name of the DTMF menu in confbridge.conf to be applied to
100 this channel. No menu is applied by default if this option is left
105 <para>Enters the user into a specified conference bridge. The user can
106 exit the conference by hangup or DTMF menu option.</para>
109 <ref type="application">ConfBridge</ref>
110 <ref type="function">CONFBRIDGE</ref>
111 <ref type="function">CONFBRIDGE_INFO</ref>
114 <function name="CONFBRIDGE" language="en_US">
116 Set a custom dynamic bridge and user profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
119 <parameter name="type" required="true">
120 <para>Type refers to which type of profile the option belongs too. Type can be <literal>bridge</literal> or <literal>user</literal>.</para>
122 <parameter name="option" required="true">
123 <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel.</para>
127 <para>---- Example 1 ----</para>
128 <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
129 <para>exten => 1,1,Answer() </para>
130 <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
131 <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
132 <para>exten => 1,n,ConfBridge(1) </para>
133 <para>---- Example 2 ----</para>
134 <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>
135 <para>exten => 1,1,Answer() </para>
136 <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
137 <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
138 <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
139 <para>exten => 1,n,ConfBridge(1)</para>
142 <function name="CONFBRIDGE_INFO" language="en_US">
144 Get information about a ConfBridge conference.
147 <parameter name="type" required="true">
148 <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
150 <parameter name="conf" required="true">
151 <para>Conf refers to the name of the conference being referenced.</para>
155 <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>
158 <manager name="ConfbridgeList" language="en_US">
160 List participants in a conference.
163 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
164 <parameter name="Conference" required="false">
165 <para>Conference number.</para>
169 <para>Lists all users in a particular ConfBridge conference.
170 ConfbridgeList will follow as separate events, followed by a final event called
171 ConfbridgeListComplete.</para>
174 <manager name="ConfbridgeListRooms" language="en_US">
176 List active conferences.
179 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
182 <para>Lists data about all active conferences.
183 ConfbridgeListRooms will follow as separate events, followed by a final event called
184 ConfbridgeListRoomsComplete.</para>
187 <manager name="ConfbridgeMute" language="en_US">
189 Mute a Confbridge user.
192 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
193 <parameter name="Conference" required="true" />
194 <parameter name="Channel" required="true" />
199 <manager name="ConfbridgeUnmute" language="en_US">
201 Unmute a Confbridge user.
204 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
205 <parameter name="Conference" required="true" />
206 <parameter name="Channel" required="true" />
211 <manager name="ConfbridgeKick" language="en_US">
213 Kick a Confbridge user.
216 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
217 <parameter name="Conference" required="true" />
218 <parameter name="Channel" required="true" />
223 <manager name="ConfbridgeLock" language="en_US">
225 Lock a Confbridge conference.
228 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
229 <parameter name="Conference" required="true" />
234 <manager name="ConfbridgeUnlock" language="en_US">
236 Unlock a Confbridge conference.
239 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
240 <parameter name="Conference" required="true" />
245 <manager name="ConfbridgeStartRecord" language="en_US">
247 Start recording a Confbridge conference.
250 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
251 <parameter name="Conference" required="true" />
252 <parameter name="RecordFile" required="false" />
255 <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>
258 <manager name="ConfbridgeStopRecord" language="en_US">
260 Stop recording a Confbridge conference.
263 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
264 <parameter name="Conference" required="true" />
269 <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
271 Set a conference user as the single video source distributed to all other participants.
274 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
275 <parameter name="Conference" required="true" />
276 <parameter name="Channel" required="true" />
285 * \par Playing back a file to a channel in a conference
286 * You might notice in this application that while playing a sound file
287 * to a channel the actual conference bridge lock is not held. This is done so
288 * that other channels are not blocked from interacting with the conference bridge.
289 * Unfortunately because of this it is possible for things to change after the sound file
290 * is done being played. Data must therefore be checked after reacquiring the conference
291 * bridge lock if it is important.
294 static const char app[] = "ConfBridge";
296 /* Number of buckets our conference bridges container can have */
297 #define CONFERENCE_BRIDGE_BUCKETS 53
300 CONF_RECORD_EXIT = 0,
305 /*! \brief Container to hold all conference bridges in progress */
306 static struct ao2_container *conference_bridges;
308 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
309 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number);
310 static int execute_menu_entry(struct conference_bridge *conference_bridge,
311 struct conference_bridge_user *conference_bridge_user,
312 struct ast_bridge_channel *bridge_channel,
313 struct conf_menu_entry *menu_entry,
314 struct conf_menu *menu);
316 /*! \brief Hashing function used for conference bridges container */
317 static int conference_bridge_hash_cb(const void *obj, const int flags)
319 const struct conference_bridge *conference_bridge = obj;
320 return ast_str_case_hash(conference_bridge->name);
323 /*! \brief Comparison function used for conference bridges container */
324 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
326 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
327 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
330 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
333 case CONF_SOUND_HAS_JOINED:
334 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
335 case CONF_SOUND_HAS_LEFT:
336 return S_OR(custom_sounds->hasleft, "conf-hasleft");
337 case CONF_SOUND_KICKED:
338 return S_OR(custom_sounds->kicked, "conf-kicked");
339 case CONF_SOUND_MUTED:
340 return S_OR(custom_sounds->muted, "conf-muted");
341 case CONF_SOUND_UNMUTED:
342 return S_OR(custom_sounds->unmuted, "conf-unmuted");
343 case CONF_SOUND_ONLY_ONE:
344 return S_OR(custom_sounds->onlyone, "conf-onlyone");
345 case CONF_SOUND_THERE_ARE:
346 return S_OR(custom_sounds->thereare, "conf-thereare");
347 case CONF_SOUND_OTHER_IN_PARTY:
348 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
349 case CONF_SOUND_PLACE_IN_CONF:
350 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
351 case CONF_SOUND_WAIT_FOR_LEADER:
352 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
353 case CONF_SOUND_LEADER_HAS_LEFT:
354 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
355 case CONF_SOUND_GET_PIN:
356 return S_OR(custom_sounds->getpin, "conf-getpin");
357 case CONF_SOUND_INVALID_PIN:
358 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
359 case CONF_SOUND_ONLY_PERSON:
360 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
361 case CONF_SOUND_LOCKED:
362 return S_OR(custom_sounds->locked, "conf-locked");
363 case CONF_SOUND_LOCKED_NOW:
364 return S_OR(custom_sounds->lockednow, "conf-lockednow");
365 case CONF_SOUND_UNLOCKED_NOW:
366 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
367 case CONF_SOUND_ERROR_MENU:
368 return S_OR(custom_sounds->errormenu, "conf-errormenu");
369 case CONF_SOUND_JOIN:
370 return S_OR(custom_sounds->join, "confbridge-join");
371 case CONF_SOUND_LEAVE:
372 return S_OR(custom_sounds->leave, "confbridge-leave");
373 case CONF_SOUND_PARTICIPANTS_MUTED:
374 return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
375 case CONF_SOUND_PARTICIPANTS_UNMUTED:
376 return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
382 static struct ast_frame *rec_read(struct ast_channel *ast)
384 return &ast_null_frame;
386 static int rec_write(struct ast_channel *ast, struct ast_frame *f)
390 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
391 static struct ast_channel_tech record_tech = {
392 .type = "ConfBridgeRec",
393 .description = "Conference Bridge Recording Channel",
394 .requester = rec_request,
398 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
400 struct ast_channel *tmp;
401 struct ast_format fmt;
402 const char *conf_name = data;
403 if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
404 "ConfBridgeRecorder/conf-%s-uid-%d",
406 (int) ast_random()))) {
409 ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
410 ast_channel_tech_set(tmp, &record_tech);
411 ast_format_cap_add_all(ast_channel_nativeformats(tmp));
412 ast_format_copy(ast_channel_writeformat(tmp), &fmt);
413 ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
414 ast_format_copy(ast_channel_readformat(tmp), &fmt);
415 ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
419 static void *record_thread(void *obj)
421 struct conference_bridge *conference_bridge = obj;
422 struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
423 struct ast_channel *chan;
424 struct ast_str *filename = ast_str_alloca(PATH_MAX);
426 ast_mutex_lock(&conference_bridge->record_lock);
428 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
429 conference_bridge->record_thread = AST_PTHREADT_NULL;
430 ast_mutex_unlock(&conference_bridge->record_lock);
431 ao2_ref(conference_bridge, -1);
435 /* XXX If we get an EXIT right here, START will essentially be a no-op */
436 while (conference_bridge->record_state != CONF_RECORD_EXIT) {
437 if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
438 ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
442 ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
443 conference_bridge->name,
447 chan = ast_channel_ref(conference_bridge->record_chan);
449 pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
450 ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
452 ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
453 /* STOP has been called. Wait for either a START or an EXIT */
454 ast_cond_wait(&conference_bridge->record_cond, &conference_bridge->record_lock);
456 ast_mutex_unlock(&conference_bridge->record_lock);
457 ao2_ref(conference_bridge, -1);
461 /*! \brief Returns whether or not conference is being recorded.
462 * \param conference_bridge The bridge to check for recording
463 * \retval 1, conference is recording.
464 * \retval 0, conference is NOT recording.
466 static int conf_is_recording(struct conference_bridge *conference_bridge)
468 return conference_bridge->record_state == CONF_RECORD_START;
471 /*! \brief Stop recording a conference bridge
473 * \param conference_bridge The conference bridge on which to stop the recording
477 static int conf_stop_record(struct conference_bridge *conference_bridge)
479 struct ast_channel *chan;
480 if (conference_bridge->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference_bridge)) {
483 conference_bridge->record_state = CONF_RECORD_STOP;
484 chan = ast_channel_ref(conference_bridge->record_chan);
485 ast_bridge_remove(conference_bridge->bridge, chan);
486 ast_queue_frame(chan, &ast_null_frame);
487 chan = ast_channel_unref(chan);
488 ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
495 * \brief Stops the confbridge recording thread.
497 * \note Must be called with the conference_bridge locked
499 static int conf_stop_record_thread(struct conference_bridge *conference_bridge)
501 if (conference_bridge->record_thread == AST_PTHREADT_NULL) {
504 conf_stop_record(conference_bridge);
506 ast_mutex_lock(&conference_bridge->record_lock);
507 conference_bridge->record_state = CONF_RECORD_EXIT;
508 ast_cond_signal(&conference_bridge->record_cond);
509 ast_mutex_unlock(&conference_bridge->record_lock);
511 pthread_join(conference_bridge->record_thread, NULL);
512 conference_bridge->record_thread = AST_PTHREADT_NULL;
514 /* this is the reference given to the channel during the channel alloc */
515 if (conference_bridge->record_chan) {
516 conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan);
522 /*! \brief Start recording the conference
524 * \note conference_bridge must be locked when calling this function
525 * \param conference_bridge The conference bridge to start recording
527 * \rteval non-zero failure
529 static int conf_start_record(struct conference_bridge *conference_bridge)
531 struct ast_format_cap *cap;
532 struct ast_format tmpfmt;
535 if (conference_bridge->record_state != CONF_RECORD_STOP) {
539 if (!pbx_findapp("MixMonitor")) {
540 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
544 if (!(cap = ast_format_cap_alloc_nolock())) {
548 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
550 if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) {
551 cap = ast_format_cap_destroy(cap);
555 cap = ast_format_cap_destroy(cap);
557 conference_bridge->record_state = CONF_RECORD_START;
558 ast_mutex_lock(&conference_bridge->record_lock);
559 ast_cond_signal(&conference_bridge->record_cond);
560 ast_mutex_unlock(&conference_bridge->record_lock);
561 ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
566 /*! \brief Start the recording thread on a conference bridge
568 * \param conference_bridge The conference bridge on which to start the recording thread
572 static int start_conf_record_thread(struct conference_bridge *conference_bridge)
574 ao2_ref(conference_bridge, +1); /* give the record thread a ref */
576 ao2_lock(conference_bridge);
577 conf_start_record(conference_bridge);
578 ao2_unlock(conference_bridge);
580 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
581 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
582 ao2_ref(conference_bridge, -1); /* error so remove ref */
589 static void send_conf_start_event(const char *conf_name)
592 <managerEventInstance>
593 <synopsis>Raised when a conference starts.</synopsis>
595 <parameter name="Conference">
596 <para>The name of the Confbridge conference.</para>
600 <ref type="managerEvent">ConfbridgeEnd</ref>
602 </managerEventInstance>
604 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
607 static void send_conf_end_event(const char *conf_name)
610 <managerEventInstance>
611 <synopsis>Raised when a conference ends.</synopsis>
613 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
616 <ref type="managerEvent">ConfbridgeStart</ref>
617 <ref type="application">ConfBridge</ref>
619 </managerEventInstance>
621 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
624 static void send_join_event(struct ast_channel *chan, const char *conf_name)
627 <managerEventInstance>
628 <synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
630 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
633 <ref type="managerEvent">ConfbridgeLeave</ref>
634 <ref type="application">ConfBridge</ref>
636 </managerEventInstance>
638 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
642 "CallerIDnum: %s\r\n"
643 "CallerIDname: %s\r\n",
644 ast_channel_name(chan),
645 ast_channel_uniqueid(chan),
647 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
648 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
652 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
655 <managerEventInstance>
656 <synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
658 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
661 <ref type="managerEvent">ConfbridgeJoin</ref>
663 </managerEventInstance>
665 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
669 "CallerIDnum: %s\r\n"
670 "CallerIDname: %s\r\n",
671 ast_channel_name(chan),
672 ast_channel_uniqueid(chan),
674 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
675 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
680 * \brief Announce number of users in the conference bridge to the caller
682 * \param conference_bridge Conference bridge to peek at
683 * \param conference_bridge_user Optional Caller
685 * \note if caller is NULL, the announcment will be sent to all participants in the conference.
686 * \return Returns 0 on success, -1 if the user hung up
688 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
690 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
691 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
692 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
694 if (conference_bridge->activeusers <= 1) {
695 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
697 } else if (conference_bridge->activeusers == 2) {
698 if (conference_bridge_user) {
699 /* Eep, there is one other person */
700 if (ast_stream_and_wait(conference_bridge_user->chan,
706 play_sound_file(conference_bridge, only_one);
709 /* Alas multiple others in here */
710 if (conference_bridge_user) {
711 if (ast_stream_and_wait(conference_bridge_user->chan,
716 if (ast_say_number(conference_bridge_user->chan, conference_bridge->activeusers - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) {
719 if (ast_stream_and_wait(conference_bridge_user->chan,
724 } else if (ast_fileexists(there_are, NULL, NULL) && ast_fileexists(other_in_party, NULL, NULL)) {
725 play_sound_file(conference_bridge, there_are);
726 play_sound_number(conference_bridge, conference_bridge->activeusers - 1);
727 play_sound_file(conference_bridge, other_in_party);
734 * \brief Play back an audio file to a channel
736 * \param conference_bridge Conference bridge they are in
737 * \param chan Channel to play audio prompt to
738 * \param file Prompt to play
740 * \return Returns 0 on success, -1 if the user hung up
741 * \note Generally this should be called when the conference is unlocked to avoid blocking
742 * the entire conference while the sound is played. But don't unlock the conference bridge
743 * in the middle of a state transition.
745 static int play_prompt_to_user(struct conference_bridge_user *cbu, const char *filename)
747 return ast_stream_and_wait(cbu->chan, filename, "");
750 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
752 /* Right now, only marked users are automatically set as the single src of video.*/
757 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
759 struct conference_bridge_user *tmp_user = NULL;
760 ao2_lock(conference_bridge);
761 /* see if anyone is already the video src */
762 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
763 if (tmp_user->chan == chan) {
766 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
771 ao2_unlock(conference_bridge);
773 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
775 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
776 /* we joined and are video capable, we override anyone else that may have already been the video feed */
777 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
781 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
783 struct conference_bridge_user *tmp_user = NULL;
785 /* if this isn't a video source, nothing to update */
786 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
790 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
792 /* If in follow talker mode, make sure to restore this mode on the
793 * bridge when a source is removed. It is possible this channel was
794 * only set temporarily as a video source by an AMI or DTMF action. */
795 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
796 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
799 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
800 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
801 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
805 /* Make the next available marked user the video src. */
806 ao2_lock(conference_bridge);
807 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
808 if (tmp_user->chan == chan) {
811 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
812 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
816 ao2_unlock(conference_bridge);
820 * \brief Destroy a conference bridge
822 * \param obj The conference bridge object
824 * \return Returns nothing
826 static void destroy_conference_bridge(void *obj)
828 struct conference_bridge *conference_bridge = obj;
830 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
832 ast_mutex_destroy(&conference_bridge->playback_lock);
834 if (conference_bridge->playback_chan) {
835 struct ast_channel *underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
836 if (underlying_channel) {
837 ast_hangup(underlying_channel);
839 ast_hangup(conference_bridge->playback_chan);
840 conference_bridge->playback_chan = NULL;
843 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
844 if (conference_bridge->bridge) {
845 ast_bridge_destroy(conference_bridge->bridge);
846 conference_bridge->bridge = NULL;
848 conf_bridge_profile_destroy(&conference_bridge->b_profile);
851 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
853 * \param cbu The conference bridge user that is joining
857 static int handle_conf_user_join(struct conference_bridge_user *cbu)
859 conference_event_fn handler;
860 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
861 handler = cbu->conference_bridge->state->join_marked;
862 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
863 handler = cbu->conference_bridge->state->join_waitmarked;
865 handler = cbu->conference_bridge->state->join_unmarked;
868 ast_assert(handler != NULL);
871 conf_invalid_event_fn(cbu);
880 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
882 * \param cbu The conference bridge user that is leaving
886 static int handle_conf_user_leave(struct conference_bridge_user *cbu)
888 conference_event_fn handler;
889 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
890 handler = cbu->conference_bridge->state->leave_marked;
891 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
892 handler = cbu->conference_bridge->state->leave_waitmarked;
894 handler = cbu->conference_bridge->state->leave_unmarked;
897 ast_assert(handler != NULL);
900 /* This should never happen. If it does, though, it is bad. The user will not have been removed
901 * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
902 * Shouldn't happen, though. */
903 conf_invalid_event_fn(cbu);
912 int conf_handle_first_marked_common(struct conference_bridge_user *cbu)
914 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))) {
920 int conf_handle_inactive_waitmarked(struct conference_bridge_user *cbu)
922 /* Be sure we are muted so we can't talk to anybody else waiting */
923 cbu->features.mute = 1;
924 /* If we have not been quieted play back that they are waiting for the leader */
925 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu,
926 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, cbu->b_profile.sounds))) {
927 /* user hungup while the sound was playing */
930 /* Start music on hold if needed */
931 if (ast_test_flag(&cbu->u_profile, USER_OPT_MUSICONHOLD)) {
932 ast_moh_start(cbu->chan, cbu->u_profile.moh_class, NULL);
933 cbu->playing_moh = 1;
938 int conf_handle_only_unmarked(struct conference_bridge_user *cbu)
940 /* If audio prompts have not been quieted or this prompt quieted play it on out */
941 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
942 if (play_prompt_to_user(cbu,
943 conf_get_sound(CONF_SOUND_ONLY_PERSON, cbu->b_profile.sounds))) {
944 /* user hungup while the sound was playing */
951 int conf_add_post_join_action(struct conference_bridge_user *cbu, int (*func)(struct conference_bridge_user *cbu))
953 struct post_join_action *action;
954 if (!(action = ast_calloc(1, sizeof(*action)))) {
958 AST_LIST_INSERT_TAIL(&cbu->post_join_list, action, list);
963 void conf_handle_first_join(struct conference_bridge *conference_bridge)
965 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
968 void conf_handle_second_active(struct conference_bridge *conference_bridge)
970 /* If we are the second participant we may need to stop music on hold on the first */
971 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->active_list);
973 /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
974 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
975 first_participant->playing_moh = 0;
976 ast_moh_stop(first_participant->chan);
977 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
979 if (!ast_test_flag(&first_participant->u_profile, USER_OPT_STARTMUTED)) {
980 first_participant->features.mute = 0;
984 void conf_ended(struct conference_bridge *conference_bridge)
986 /* Called with a reference to conference_bridge */
987 ao2_unlink(conference_bridges, conference_bridge);
988 send_conf_end_event(conference_bridge->name);
989 conf_stop_record_thread(conference_bridge);
993 * \brief Join a conference bridge
995 * \param name The conference name
996 * \param conference_bridge_user Conference bridge user structure
998 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1000 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
1002 struct conference_bridge *conference_bridge = NULL;
1003 struct post_join_action *action;
1004 struct conference_bridge tmp;
1005 int max_members_reached = 0;
1007 ast_copy_string(tmp.name, name, sizeof(tmp.name));
1009 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
1010 ao2_lock(conference_bridges);
1012 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
1014 /* Attempt to find an existing conference bridge */
1015 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1017 if (conference_bridge && conference_bridge->b_profile.max_members) {
1018 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->activeusers ? 0 : 1;
1021 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1022 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
1023 ao2_unlock(conference_bridges);
1024 ao2_ref(conference_bridge, -1);
1025 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
1026 ast_stream_and_wait(conference_bridge_user->chan,
1027 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
1032 /* If no conference bridge was found see if we can create one */
1033 if (!conference_bridge) {
1034 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1035 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
1036 ao2_unlock(conference_bridges);
1037 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
1041 /* Setup conference bridge parameters */
1042 conference_bridge->record_thread = AST_PTHREADT_NULL;
1043 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
1044 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
1046 /* Create an actual bridge that will do the audio mixing */
1047 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
1048 ao2_ref(conference_bridge, -1);
1049 conference_bridge = NULL;
1050 ao2_unlock(conference_bridges);
1051 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
1055 /* Set the internal sample rate on the bridge from the bridge profile */
1056 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
1057 /* Set the internal mixing interval on the bridge from the bridge profile */
1058 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
1060 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1061 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
1064 /* Setup lock for playback channel */
1065 ast_mutex_init(&conference_bridge->playback_lock);
1067 /* Setup lock for the record channel */
1068 ast_mutex_init(&conference_bridge->record_lock);
1069 ast_cond_init(&conference_bridge->record_cond, NULL);
1071 /* Link it into the conference bridges container */
1072 ao2_link(conference_bridges, conference_bridge);
1074 /* Set the initial state to EMPTY */
1075 conference_bridge->state = CONF_STATE_EMPTY;
1077 conference_bridge->record_state = CONF_RECORD_STOP;
1078 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
1079 ao2_lock(conference_bridge);
1080 start_conf_record_thread(conference_bridge);
1081 ao2_unlock(conference_bridge);
1084 send_conf_start_event(conference_bridge->name);
1085 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
1088 ao2_unlock(conference_bridges);
1090 /* Setup conference bridge user parameters */
1091 conference_bridge_user->conference_bridge = conference_bridge;
1093 ao2_lock(conference_bridge);
1095 if (handle_conf_user_join(conference_bridge_user)) {
1096 /* Invalid event, nothing was done, so we don't want to process a leave. */
1097 ao2_unlock(conference_bridge);
1098 ao2_ref(conference_bridge, -1);
1102 if (ast_check_hangup(conference_bridge_user->chan)) {
1103 ao2_unlock(conference_bridge);
1104 leave_conference_bridge(conference_bridge, conference_bridge_user);
1108 ao2_unlock(conference_bridge);
1110 /* Announce number of users if need be */
1111 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
1112 if (announce_user_count(conference_bridge, conference_bridge_user)) {
1113 leave_conference_bridge(conference_bridge, conference_bridge_user);
1118 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
1119 (conference_bridge->activeusers > conference_bridge_user->u_profile.announce_user_count_all_after)) {
1120 if (announce_user_count(conference_bridge, NULL)) {
1121 leave_conference_bridge(conference_bridge, conference_bridge_user);
1126 /* Handle post-join actions */
1127 while ((action = AST_LIST_REMOVE_HEAD(&conference_bridge_user->post_join_list, list))) {
1128 action->func(conference_bridge_user);
1132 return conference_bridge;
1136 * \brief Leave a conference bridge
1138 * \param conference_bridge The conference bridge to leave
1139 * \param conference_bridge_user The conference bridge user structure
1142 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1144 ao2_lock(conference_bridge);
1146 handle_conf_user_leave(conference_bridge_user);
1148 /* Done mucking with the conference bridge, huzzah */
1149 ao2_unlock(conference_bridge);
1150 ao2_ref(conference_bridge, -1);
1155 * \brief allocates playback chan on a channel
1156 * \pre expects conference to be locked before calling this function
1158 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
1161 struct ast_format_cap *cap;
1162 struct ast_format tmpfmt;
1164 if (conference_bridge->playback_chan) {
1167 if (!(cap = ast_format_cap_alloc_nolock())) {
1170 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
1171 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
1172 cap = ast_format_cap_destroy(cap);
1175 cap = ast_format_cap_destroy(cap);
1177 ast_channel_internal_bridge_set(conference_bridge->playback_chan, conference_bridge->bridge);
1179 if (ast_call(conference_bridge->playback_chan, "", 0)) {
1180 ast_hangup(conference_bridge->playback_chan);
1181 conference_bridge->playback_chan = NULL;
1185 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
1189 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
1191 struct ast_channel *underlying_channel;
1193 /* Do not waste resources trying to play files that do not exist */
1194 if (!ast_strlen_zero(filename) && !ast_fileexists(filename, NULL, NULL)) {
1195 ast_log(LOG_WARNING, "File %s does not exist in any format\n", !ast_strlen_zero(filename) ? filename : "<unknown>");
1199 ast_mutex_lock(&conference_bridge->playback_lock);
1200 if (!(conference_bridge->playback_chan)) {
1201 if (alloc_playback_chan(conference_bridge)) {
1202 ast_mutex_unlock(&conference_bridge->playback_lock);
1205 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1207 /* Channel was already available so we just need to add it back into the bridge */
1208 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1209 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0);
1212 /* The channel is all under our control, in goes the prompt */
1213 if (!ast_strlen_zero(filename)) {
1214 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
1215 } else if (say_number >= 0) {
1216 ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL);
1219 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge);
1220 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
1222 ast_mutex_unlock(&conference_bridge->playback_lock);
1227 int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
1229 return play_sound_helper(conference_bridge, filename, -1);
1233 * \brief Play number into the conference bridge
1235 * \param conference_bridge The conference bridge to say the number into
1236 * \param say_number number to say
1239 * \retval -1 failure
1241 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
1243 return play_sound_helper(conference_bridge, NULL, say_number);
1246 static void conf_handle_talker_destructor(void *pvt_data)
1251 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
1253 char *conf_name = pvt_data;
1256 switch (bridge_channel->state) {
1257 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
1260 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
1264 return; /* uhh this shouldn't happen, but bail if it does. */
1267 /* notify AMI someone is has either started or stopped talking */
1269 <managerEventInstance>
1270 <synopsis>Raised when a conference participant has started or stopped talking.</synopsis>
1272 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
1273 <parameter name="TalkingStatus">
1280 </managerEventInstance>
1282 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
1285 "Conference: %s\r\n"
1286 "TalkingStatus: %s\r\n",
1287 ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
1290 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
1292 char pin_guess[MAX_PIN+1] = { 0, };
1293 const char *pin = conference_bridge_user->u_profile.pin;
1294 char *tmp = pin_guess;
1296 unsigned int len = MAX_PIN ;
1298 /* give them three tries to get the pin right */
1299 for (i = 0; i < 3; i++) {
1300 if (ast_app_getdata(chan,
1301 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
1302 tmp, len, 0) >= 0) {
1303 if (!strcasecmp(pin, pin_guess)) {
1307 ast_streamfile(chan,
1308 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
1309 ast_channel_language(chan));
1310 res = ast_waitstream(chan, AST_DIGIT_ANY);
1312 /* Account for digit already read during ivalid pin playback
1313 * resetting pin buf. */
1315 pin_guess[1] = '\0';
1316 tmp = pin_guess + 1;
1319 /* reset pin buf as empty buffer. */
1327 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
1329 char destdir[PATH_MAX];
1333 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1335 if (ast_mkdir(destdir, 0777) != 0) {
1336 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1339 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1340 "%s/confbridge-name-%s-%s", destdir,
1341 conf_name, ast_channel_uniqueid(user->chan));
1343 res = ast_play_and_record(user->chan,
1345 user->name_rec_location,
1350 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1355 user->name_rec_location[0] = '\0';
1361 /*! \brief The ConfBridge application */
1362 static int confbridge_exec(struct ast_channel *chan, const char *data)
1364 int res = 0, volume_adjustments[2];
1367 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
1368 const char *u_profile_name = DEFAULT_USER_PROFILE;
1369 struct conference_bridge *conference_bridge = NULL;
1370 struct conference_bridge_user conference_bridge_user = {
1372 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1373 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1374 .tech_args.drop_silence = 0,
1376 AST_DECLARE_APP_ARGS(args,
1377 AST_APP_ARG(conf_name);
1378 AST_APP_ARG(b_profile_name);
1379 AST_APP_ARG(u_profile_name);
1380 AST_APP_ARG(menu_name);
1382 ast_bridge_features_init(&conference_bridge_user.features);
1384 if (ast_channel_state(chan) != AST_STATE_UP) {
1388 if (ast_strlen_zero(data)) {
1389 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1390 res = -1; /* invalid PIN */
1391 goto confbridge_cleanup;
1394 /* We need to make a copy of the input string if we are going to modify it! */
1395 parse = ast_strdupa(data);
1397 AST_STANDARD_APP_ARGS(args, parse);
1399 /* bridge profile name */
1400 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1401 b_profile_name = args.b_profile_name;
1403 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
1404 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
1406 goto confbridge_cleanup;
1409 /* user profile name */
1410 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1411 u_profile_name = args.u_profile_name;
1414 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
1415 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
1417 goto confbridge_cleanup;
1420 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
1422 /* ask for a PIN immediately after finding user profile. This has to be
1423 * prompted for requardless of quiet setting. */
1424 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
1425 if (conf_get_pin(chan, &conference_bridge_user)) {
1426 res = -1; /* invalid PIN */
1427 goto confbridge_cleanup;
1431 /* See if we need them to record a intro name */
1432 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
1433 conf_rec_name(&conference_bridge_user, args.conf_name);
1437 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
1438 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
1439 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
1440 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
1442 res = -1; /* invalid PIN */
1443 goto confbridge_cleanup;
1447 /* Set if DTMF should pass through for this user or not */
1448 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
1449 conference_bridge_user.features.dtmf_passthrough = 1;
1452 /* Set dsp threshold values if present */
1453 if (conference_bridge_user.u_profile.talking_threshold) {
1454 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
1456 if (conference_bridge_user.u_profile.silence_threshold) {
1457 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
1460 /* Set a talker indicate call back if talking detection is requested */
1461 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
1462 char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
1464 res = -1; /* invalid PIN */
1465 goto confbridge_cleanup;
1467 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
1468 conf_handle_talker_cb,
1469 conf_handle_talker_destructor,
1473 /* Look for a conference bridge matching the provided name */
1474 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
1475 res = -1; /* invalid PIN */
1476 goto confbridge_cleanup;
1479 /* Keep a copy of volume adjustments so we can restore them later if need be */
1480 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1481 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1483 /* If the caller should be joined already muted, make it so */
1484 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
1485 conference_bridge_user.features.mute = 1;
1488 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
1489 conference_bridge_user.tech_args.drop_silence = 1;
1492 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
1494 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1496 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1500 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
1502 /* Reduce background noise from each participant */
1503 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1504 ast_free(mod_speex);
1505 ast_func_write(chan, "DENOISE(rx)", "on");
1509 /* if this user has a intro, play it before entering */
1510 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1511 ast_autoservice_start(chan);
1512 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1513 play_sound_file(conference_bridge,
1514 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
1515 ast_autoservice_stop(chan);
1518 /* Play the Join sound to both the conference and the user entering. */
1520 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
1521 if (conference_bridge_user.playing_moh) {
1524 ast_stream_and_wait(chan, join_sound, "");
1525 ast_autoservice_start(chan);
1526 play_sound_file(conference_bridge, join_sound);
1527 ast_autoservice_stop(chan);
1528 if (conference_bridge_user.playing_moh) {
1529 ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
1533 /* See if we need to automatically set this user as a video source or not */
1534 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
1536 /* Join our conference bridge for real */
1537 send_join_event(conference_bridge_user.chan, conference_bridge->name);
1538 ast_bridge_join(conference_bridge->bridge,
1541 &conference_bridge_user.features,
1542 &conference_bridge_user.tech_args);
1543 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
1545 /* if we're shutting down, don't attempt to do further processing */
1546 if (ast_shutting_down()) {
1547 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1548 conference_bridge = NULL;
1549 goto confbridge_cleanup;
1552 /* If this user was a video source, we need to clean up and possibly pick a new source. */
1553 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
1555 /* if this user has a intro, play it when leaving */
1556 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1557 ast_autoservice_start(chan);
1558 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1559 play_sound_file(conference_bridge,
1560 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
1561 ast_autoservice_stop(chan);
1564 /* play the leave sound */
1566 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
1567 ast_autoservice_start(chan);
1568 play_sound_file(conference_bridge, leave_sound);
1569 ast_autoservice_stop(chan);
1572 /* Easy as pie, depart this channel from the conference bridge */
1573 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1574 conference_bridge = NULL;
1576 /* If the user was kicked from the conference play back the audio prompt for it */
1577 if (!quiet && conference_bridge_user.kicked) {
1578 res = ast_stream_and_wait(chan,
1579 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
1583 /* Restore volume adjustments to previous values in case they were changed */
1584 if (volume_adjustments[0]) {
1585 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1587 if (volume_adjustments[1]) {
1588 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1591 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1592 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
1596 ast_bridge_features_cleanup(&conference_bridge_user.features);
1597 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
1601 static int action_toggle_mute(struct conference_bridge *conference_bridge,
1602 struct conference_bridge_user *conference_bridge_user,
1603 struct ast_channel *chan)
1605 /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
1606 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
1607 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
1608 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));
1610 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
1611 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
1612 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
1616 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1618 struct conference_bridge_user *participant = NULL;
1619 const char *sound_to_play;
1621 ao2_lock(conference_bridge);
1623 /* If already muted, then unmute */
1624 conference_bridge->muted = conference_bridge->muted ? 0 : 1;
1625 sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
1626 conference_bridge_user->b_profile.sounds);
1628 AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) {
1629 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
1630 participant->features.mute = conference_bridge->muted;
1634 ao2_unlock(conference_bridge);
1636 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
1637 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
1639 /* Announce to the group that all participants are muted */
1640 ast_autoservice_start(conference_bridge_user->chan);
1641 play_sound_helper(conference_bridge, sound_to_play, 0);
1642 ast_autoservice_stop(conference_bridge_user->chan);
1647 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1649 char *file_copy = ast_strdupa(playback_file);
1652 while ((file = strsep(&file_copy, "&"))) {
1653 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1654 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1661 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
1662 struct conference_bridge_user *conference_bridge_user,
1663 struct ast_bridge_channel *bridge_channel,
1664 struct conf_menu *menu,
1665 const char *playback_file,
1666 const char *cur_dtmf,
1671 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1672 struct conf_menu_entry new_menu_entry = { { 0, }, };
1673 char *file_copy = ast_strdupa(playback_file);
1676 while ((file = strsep(&file_copy, "&"))) {
1677 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
1678 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1682 /* now wait for more digits. */
1683 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1684 /* streaming finished and no DTMF was entered */
1686 } else if (digit == -1) {
1690 break; /* dtmf was entered */
1694 /* streaming finished on all files and no DTMF was entered */
1697 ast_stopstream(bridge_channel->chan);
1699 /* If we get here, then DTMF has been entered, This means no
1700 * additional prompts should be played for this menu entry */
1703 /* If a digit was pressed during the payback, update
1704 * the dtmf string and look for a new menu entry in the
1706 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1707 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1708 dtmf[i] = cur_dtmf[i];
1710 dtmf[i] = (char) digit;
1716 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1717 * If this is the case, no new DTMF sequence should be looked for. */
1722 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1723 execute_menu_entry(conference_bridge,
1724 conference_bridge_user,
1726 &new_menu_entry, menu);
1727 conf_menu_entry_destroy(&new_menu_entry);
1732 static int action_kick_last(struct conference_bridge *conference_bridge,
1733 struct ast_bridge_channel *bridge_channel,
1734 struct conference_bridge_user *conference_bridge_user)
1736 struct conference_bridge_user *last_participant = NULL;
1737 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1740 ast_stream_and_wait(bridge_channel->chan,
1741 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1743 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1744 ast_channel_name(bridge_channel->chan),
1745 conference_bridge->name);
1749 ao2_lock(conference_bridge);
1750 if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user)
1751 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
1752 ao2_unlock(conference_bridge);
1753 ast_stream_and_wait(bridge_channel->chan,
1754 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1756 } else if (last_participant) {
1757 last_participant->kicked = 1;
1758 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
1759 ao2_unlock(conference_bridge);
1764 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1766 struct ast_pbx_args args;
1767 struct ast_pbx *pbx;
1773 memset(&args, 0, sizeof(args));
1774 args.no_hangup_chan = 1;
1776 ast_channel_lock(bridge_channel->chan);
1779 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
1780 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
1781 priority = ast_channel_priority(bridge_channel->chan);
1782 pbx = ast_channel_pbx(bridge_channel->chan);
1783 ast_channel_pbx_set(bridge_channel->chan, NULL);
1786 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
1787 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
1788 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
1790 ast_channel_unlock(bridge_channel->chan);
1793 res = ast_pbx_run_args(bridge_channel->chan, &args);
1796 ast_channel_lock(bridge_channel->chan);
1798 ast_channel_exten_set(bridge_channel->chan, exten);
1799 ast_channel_context_set(bridge_channel->chan, context);
1800 ast_channel_priority_set(bridge_channel->chan, priority);
1801 ast_channel_pbx_set(bridge_channel->chan, pbx);
1803 ast_channel_unlock(bridge_channel->chan);
1808 static int execute_menu_entry(struct conference_bridge *conference_bridge,
1809 struct conference_bridge_user *conference_bridge_user,
1810 struct ast_bridge_channel *bridge_channel,
1811 struct conf_menu_entry *menu_entry,
1812 struct conf_menu *menu)
1814 struct conf_menu_action *menu_action;
1815 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1816 int stop_prompts = 0;
1819 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1820 switch (menu_action->id) {
1821 case MENU_ACTION_TOGGLE_MUTE:
1822 res |= action_toggle_mute(conference_bridge,
1823 conference_bridge_user,
1824 bridge_channel->chan);
1826 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1830 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
1832 case MENU_ACTION_PARTICIPANT_COUNT:
1833 announce_user_count(conference_bridge, conference_bridge_user);
1835 case MENU_ACTION_PLAYBACK:
1836 if (!stop_prompts) {
1837 res |= action_playback(bridge_channel, menu_action->data.playback_file);
1840 case MENU_ACTION_RESET_LISTENING:
1841 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
1843 case MENU_ACTION_RESET_TALKING:
1844 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
1846 case MENU_ACTION_INCREASE_LISTENING:
1847 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1848 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
1850 case MENU_ACTION_DECREASE_LISTENING:
1851 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1852 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
1854 case MENU_ACTION_INCREASE_TALKING:
1855 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1856 AST_AUDIOHOOK_DIRECTION_READ, 1);
1858 case MENU_ACTION_DECREASE_TALKING:
1859 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1860 AST_AUDIOHOOK_DIRECTION_READ, -1);
1862 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1863 if (!(stop_prompts)) {
1864 res |= action_playback_and_continue(conference_bridge,
1865 conference_bridge_user,
1868 menu_action->data.playback_file,
1873 case MENU_ACTION_DIALPLAN_EXEC:
1874 res |= action_dialplan_exec(bridge_channel, menu_action);
1876 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1880 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
1881 res |= ast_stream_and_wait(bridge_channel->chan,
1882 (conference_bridge->locked ?
1883 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
1884 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
1888 case MENU_ACTION_ADMIN_KICK_LAST:
1889 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
1891 case MENU_ACTION_LEAVE:
1892 ao2_lock(conference_bridge);
1893 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
1894 ao2_unlock(conference_bridge);
1896 case MENU_ACTION_NOOP:
1898 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1899 ao2_lock(conference_bridge);
1900 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
1901 ao2_unlock(conference_bridge);
1903 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1904 handle_video_on_exit(conference_bridge, bridge_channel->chan);
1911 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
1912 struct conference_bridge_user *conference_bridge_user,
1913 struct conf_menu_entry *menu_entry,
1914 struct conf_menu *menu)
1916 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
1918 /* See if music on hold is playing */
1919 ao2_lock(conference_bridge);
1920 if (conference_bridge_user->playing_moh) {
1921 /* MOH is going, let's stop it */
1922 ast_moh_stop(bridge_channel->chan);
1924 ao2_unlock(conference_bridge);
1926 /* execute the list of actions associated with this menu entry */
1927 execute_menu_entry(conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
1929 /* See if music on hold needs to be started back up again */
1930 ao2_lock(conference_bridge);
1931 if (conference_bridge_user->playing_moh) {
1932 ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL);
1934 ao2_unlock(conference_bridge);
1939 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
1942 struct conference_bridge *bridge = NULL;
1944 int wordlen = strlen(word);
1945 struct ao2_iterator i;
1947 i = ao2_iterator_init(conference_bridges, 0);
1948 while ((bridge = ao2_iterator_next(&i))) {
1949 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
1950 res = ast_strdup(bridge->name);
1951 ao2_ref(bridge, -1);
1954 ao2_ref(bridge, -1);
1956 ao2_iterator_destroy(&i);
1961 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1963 struct conference_bridge *bridge = NULL;
1964 struct conference_bridge tmp;
1965 struct conference_bridge_user *participant = NULL;
1969 e->command = "confbridge kick";
1971 "Usage: confbridge kick <conference> <channel>\n"
1972 " Kicks a channel out of the conference bridge.\n";
1976 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
1980 return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
1987 return CLI_SHOWUSAGE;
1990 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
1991 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1993 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
1997 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
1998 if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
2003 ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
2004 participant->kicked = 1;
2005 ast_bridge_remove(bridge->bridge, participant->chan);
2008 ao2_ref(bridge, -1);
2012 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2014 struct ao2_iterator i;
2015 struct conference_bridge *bridge = NULL;
2016 struct conference_bridge tmp;
2017 struct conference_bridge_user *participant = NULL;
2021 e->command = "confbridge list";
2023 "Usage: confbridge list [<name>]\n"
2024 " Lists all currently active conference bridges.\n";
2028 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2034 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
2035 ast_cli(a->fd, "================================ ====== ====== ========\n");
2036 i = ao2_iterator_init(conference_bridges, 0);
2037 while ((bridge = ao2_iterator_next(&i))) {
2038 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
2039 ao2_ref(bridge, -1);
2041 ao2_iterator_destroy(&i);
2046 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2047 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2049 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2052 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
2053 ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
2055 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2056 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
2057 ast_cli(a->fd, "%-17s", participant->u_profile.name);
2058 ast_cli(a->fd, "%-17s", participant->b_profile.name);
2059 ast_cli(a->fd, "%-17s", participant->menu_name);
2060 ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
2061 ast_cli(a->fd, "\n");
2064 ao2_ref(bridge, -1);
2068 return CLI_SHOWUSAGE;
2072 * \brief finds a conference by name and locks/unlocks.
2075 * \retval -1 conference not found
2077 static int generic_lock_unlock_helper(int lock, const char *conference)
2079 struct conference_bridge *bridge = NULL;
2080 struct conference_bridge tmp;
2083 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2084 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2089 bridge->locked = lock;
2090 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
2092 ao2_ref(bridge, -1);
2098 * \brief finds a conference user by channel name and mutes/unmutes them.
2101 * \retval -1 conference not found
2102 * \retval -2 user not found
2104 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
2106 struct conference_bridge *bridge = NULL;
2107 struct conference_bridge tmp;
2108 struct conference_bridge_user *participant = NULL;
2110 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2111 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2116 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2117 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
2122 participant->features.mute = mute;
2123 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));
2128 ao2_ref(bridge, -1);
2133 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2135 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2138 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2140 } else if (res == -2) {
2141 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2144 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2148 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2152 e->command = "confbridge mute";
2154 "Usage: confbridge mute <conference> <channel>\n";
2158 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2163 return CLI_SHOWUSAGE;
2166 cli_mute_unmute_helper(1, a);
2171 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2175 e->command = "confbridge unmute";
2177 "Usage: confbridge unmute <conference> <channel>\n";
2181 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2186 return CLI_SHOWUSAGE;
2189 cli_mute_unmute_helper(0, a);
2194 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2198 e->command = "confbridge lock";
2200 "Usage: confbridge lock <conference>\n";
2204 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2209 return CLI_SHOWUSAGE;
2211 if (generic_lock_unlock_helper(1, a->argv[2])) {
2212 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2214 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2219 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2223 e->command = "confbridge unlock";
2225 "Usage: confbridge unlock <conference>\n";
2229 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2234 return CLI_SHOWUSAGE;
2236 if (generic_lock_unlock_helper(0, a->argv[2])) {
2237 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2239 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2244 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2246 const char *rec_file = NULL;
2247 struct conference_bridge *bridge = NULL;
2248 struct conference_bridge tmp;
2252 e->command = "confbridge record start";
2254 "Usage: confbridge record start <conference> <file>\n"
2255 " <file> is optional, Otherwise the bridge profile\n"
2256 " record file will be used. If the bridge profile\n"
2257 " has no record file specified, a file will automatically\n"
2258 " be generated in the monitor directory\n";
2262 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2267 return CLI_SHOWUSAGE;
2270 rec_file = a->argv[4];
2273 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2274 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2276 ast_cli(a->fd, "Conference not found.\n");
2280 if (conf_is_recording(bridge)) {
2281 ast_cli(a->fd, "Conference is already being recorded.\n");
2283 ao2_ref(bridge, -1);
2286 if (!ast_strlen_zero(rec_file)) {
2287 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2290 if (start_conf_record_thread(bridge)) {
2291 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2293 ao2_ref(bridge, -1);
2298 ast_cli(a->fd, "Recording started\n");
2299 ao2_ref(bridge, -1);
2303 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2305 struct conference_bridge *bridge = NULL;
2306 struct conference_bridge tmp;
2311 e->command = "confbridge record stop";
2313 "Usage: confbridge record stop <conference>\n";
2317 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2322 return CLI_SHOWUSAGE;
2325 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2326 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2328 ast_cli(a->fd, "Conference not found.\n");
2332 ret = conf_stop_record(bridge);
2334 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
2335 ao2_ref(bridge, -1);
2339 static struct ast_cli_entry cli_confbridge[] = {
2340 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2341 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2342 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2343 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
2344 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2345 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2346 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2347 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2349 static struct ast_custom_function confbridge_function = {
2350 .name = "CONFBRIDGE",
2351 .write = func_confbridge_helper,
2354 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2355 static struct ast_custom_function confbridge_info_function = {
2356 .name = "CONFBRIDGE_INFO",
2357 .read = func_confbridge_info,
2360 static int action_confbridgelist(struct mansession *s, const struct message *m)
2362 const char *actionid = astman_get_header(m, "ActionID");
2363 const char *conference = astman_get_header(m, "Conference");
2364 struct conference_bridge_user *participant = NULL;
2365 struct conference_bridge *bridge = NULL;
2366 struct conference_bridge tmp;
2367 char id_text[80] = "";
2370 if (!ast_strlen_zero(actionid)) {
2371 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2373 if (ast_strlen_zero(conference)) {
2374 astman_send_error(s, m, "No Conference name provided.");
2377 if (!ao2_container_count(conference_bridges)) {
2378 astman_send_error(s, m, "No active conferences.");
2381 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2382 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2384 astman_send_error(s, m, "No Conference by that name found.");
2388 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2391 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2394 "Event: ConfbridgeList\r\n"
2396 "Conference: %s\r\n"
2397 "CallerIDNum: %s\r\n"
2398 "CallerIDName: %s\r\n"
2401 "MarkedUser: %s\r\n"
2405 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
2406 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
2407 ast_channel_name(participant->chan),
2408 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2409 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
2412 ao2_ref(bridge, -1);
2415 "Event: ConfbridgeListComplete\r\n"
2416 "EventList: Complete\r\n"
2419 "\r\n", total, id_text);
2424 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2426 const char *actionid = astman_get_header(m, "ActionID");
2427 struct conference_bridge *bridge = NULL;
2428 struct ao2_iterator i;
2429 char id_text[512] = "";
2432 if (!ast_strlen_zero(actionid)) {
2433 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2436 if (!ao2_container_count(conference_bridges)) {
2437 astman_send_error(s, m, "No active conferences.");
2441 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2443 /* Traverse the conference list */
2444 i = ao2_iterator_init(conference_bridges, 0);
2445 while ((bridge = ao2_iterator_next(&i))) {
2450 "Event: ConfbridgeListRooms\r\n"
2452 "Conference: %s\r\n"
2459 bridge->activeusers,
2460 bridge->markedusers,
2461 bridge->locked ? "Yes" : "No");
2464 ao2_ref(bridge, -1);
2466 ao2_iterator_destroy(&i);
2468 /* Send final confirmation */
2470 "Event: ConfbridgeListRoomsComplete\r\n"
2471 "EventList: Complete\r\n"
2474 "\r\n", totalitems, id_text);
2478 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2480 const char *conference = astman_get_header(m, "Conference");
2481 const char *channel = astman_get_header(m, "Channel");
2484 if (ast_strlen_zero(conference)) {
2485 astman_send_error(s, m, "No Conference name provided.");
2488 if (ast_strlen_zero(channel)) {
2489 astman_send_error(s, m, "No channel name provided.");
2492 if (!ao2_container_count(conference_bridges)) {
2493 astman_send_error(s, m, "No active conferences.");
2497 res = generic_mute_unmute_helper(mute, conference, channel);
2500 astman_send_error(s, m, "No Conference by that name found.");
2502 } else if (res == -2) {
2503 astman_send_error(s, m, "No Channel by that name found in Conference.");
2507 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2511 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2513 return action_mute_unmute_helper(s, m, 0);
2515 static int action_confbridgemute(struct mansession *s, const struct message *m)
2517 return action_mute_unmute_helper(s, m, 1);
2520 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2522 const char *conference = astman_get_header(m, "Conference");
2525 if (ast_strlen_zero(conference)) {
2526 astman_send_error(s, m, "No Conference name provided.");
2529 if (!ao2_container_count(conference_bridges)) {
2530 astman_send_error(s, m, "No active conferences.");
2533 if ((res = generic_lock_unlock_helper(lock, conference))) {
2534 astman_send_error(s, m, "No Conference by that name found.");
2537 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2540 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2542 return action_lock_unlock_helper(s, m, 0);
2544 static int action_confbridgelock(struct mansession *s, const struct message *m)
2546 return action_lock_unlock_helper(s, m, 1);
2549 static int action_confbridgekick(struct mansession *s, const struct message *m)
2551 const char *conference = astman_get_header(m, "Conference");
2552 const char *channel = astman_get_header(m, "Channel");
2553 struct conference_bridge_user *participant = NULL;
2554 struct conference_bridge *bridge = NULL;
2555 struct conference_bridge tmp;
2558 if (ast_strlen_zero(conference)) {
2559 astman_send_error(s, m, "No Conference name provided.");
2562 if (!ao2_container_count(conference_bridges)) {
2563 astman_send_error(s, m, "No active conferences.");
2566 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2567 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2569 astman_send_error(s, m, "No Conference by that name found.");
2574 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2575 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
2576 participant->kicked = 1;
2577 ast_bridge_remove(bridge->bridge, participant->chan);
2583 ao2_ref(bridge, -1);
2586 astman_send_ack(s, m, "User kicked");
2588 astman_send_error(s, m, "No Channel by that name found in Conference.");
2593 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2595 const char *conference = astman_get_header(m, "Conference");
2596 const char *recordfile = astman_get_header(m, "RecordFile");
2597 struct conference_bridge *bridge = NULL;
2598 struct conference_bridge tmp;
2600 if (ast_strlen_zero(conference)) {
2601 astman_send_error(s, m, "No Conference name provided.");
2604 if (!ao2_container_count(conference_bridges)) {
2605 astman_send_error(s, m, "No active conferences.");
2609 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2610 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2612 astman_send_error(s, m, "No Conference by that name found.");
2617 if (conf_is_recording(bridge)) {
2618 astman_send_error(s, m, "Conference is already being recorded.");
2620 ao2_ref(bridge, -1);
2624 if (!ast_strlen_zero(recordfile)) {
2625 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2628 if (start_conf_record_thread(bridge)) {
2629 astman_send_error(s, m, "Internal error starting conference recording.");
2631 ao2_ref(bridge, -1);
2636 ao2_ref(bridge, -1);
2637 astman_send_ack(s, m, "Conference Recording Started.");
2640 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2642 const char *conference = astman_get_header(m, "Conference");
2643 struct conference_bridge *bridge = NULL;
2644 struct conference_bridge tmp;
2646 if (ast_strlen_zero(conference)) {
2647 astman_send_error(s, m, "No Conference name provided.");
2650 if (!ao2_container_count(conference_bridges)) {
2651 astman_send_error(s, m, "No active conferences.");
2655 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2656 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2658 astman_send_error(s, m, "No Conference by that name found.");
2663 if (conf_stop_record(bridge)) {
2665 astman_send_error(s, m, "Internal error while stopping recording.");
2666 ao2_ref(bridge, -1);
2671 ao2_ref(bridge, -1);
2672 astman_send_ack(s, m, "Conference Recording Stopped.");
2676 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2678 const char *conference = astman_get_header(m, "Conference");
2679 const char *channel = astman_get_header(m, "Channel");
2680 struct conference_bridge_user *participant = NULL;
2681 struct conference_bridge *bridge = NULL;
2682 struct conference_bridge tmp;
2684 if (ast_strlen_zero(conference)) {
2685 astman_send_error(s, m, "No Conference name provided.");
2688 if (ast_strlen_zero(channel)) {
2689 astman_send_error(s, m, "No channel name provided.");
2692 if (!ao2_container_count(conference_bridges)) {
2693 astman_send_error(s, m, "No active conferences.");
2697 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2698 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2700 astman_send_error(s, m, "No Conference by that name found.");
2704 /* find channel and set as video src. */
2706 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2707 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
2708 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2713 ao2_ref(bridge, -1);
2715 /* do not access participant after bridge unlock. We are just
2716 * using this check to see if it was found or not */
2718 astman_send_error(s, m, "No channel by that name found in conference.");
2721 astman_send_ack(s, m, "Conference single video source set.");
2725 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2728 struct conference_bridge *bridge = NULL;
2729 struct conference_bridge_user *participant = NULL;
2730 struct conference_bridge tmp;
2732 AST_DECLARE_APP_ARGS(args,
2734 AST_APP_ARG(confno);
2737 /* parse all the required arguments and make sure they exist. */
2738 if (ast_strlen_zero(data)) {
2741 parse = ast_strdupa(data);
2742 AST_STANDARD_APP_ARGS(args, parse);
2743 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
2746 if (!ao2_container_count(conference_bridges)) {
2747 snprintf(buf, len, "0");
2750 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
2751 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2753 snprintf(buf, len, "0");
2757 /* get the correct count for the type requested */
2759 if (!strncasecmp(args.type, "parties", 7)) {
2760 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2763 } else if (!strncasecmp(args.type, "admins", 6)) {
2764 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2765 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
2769 } else if (!strncasecmp(args.type, "marked", 6)) {
2770 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2771 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
2775 } else if (!strncasecmp(args.type, "locked", 6)) {
2776 count = bridge->locked;
2778 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
2779 "parties, admins, marked, or locked.\n", args.type);
2781 snprintf(buf, len, "%d", count);
2783 ao2_ref(bridge, -1);
2787 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2789 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
2790 conference_bridge->activeusers++;
2793 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2795 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
2796 conference_bridge->activeusers++;
2797 conference_bridge->markedusers++;
2800 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2802 AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
2803 conference_bridge->waitingusers++;
2806 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2808 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
2809 conference_bridge->activeusers--;
2812 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2814 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
2815 conference_bridge->activeusers--;
2816 conference_bridge->markedusers--;
2819 void conf_mute_only_active(struct conference_bridge *conference_bridge)
2821 struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
2823 /* Turn on MOH/mute if the single participant is set up for it */
2824 if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
2825 only_participant->features.mute = 1;
2826 if (!ast_channel_internal_bridge(only_participant->chan) || !ast_bridge_suspend(conference_bridge->bridge, only_participant->chan)) {
2827 ast_moh_start(only_participant->chan, only_participant->u_profile.moh_class, NULL);
2828 only_participant->playing_moh = 1;
2829 if (ast_channel_internal_bridge(only_participant->chan)) {
2830 ast_bridge_unsuspend(conference_bridge->bridge, only_participant->chan);
2836 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2838 AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
2839 conference_bridge->waitingusers--;
2842 /*! \brief Called when module is being unloaded */
2843 static int unload_module(void)
2845 int res = ast_unregister_application(app);
2847 ast_custom_function_unregister(&confbridge_function);
2848 ast_custom_function_unregister(&confbridge_info_function);
2850 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2852 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
2853 ao2_ref(conference_bridges, -1);
2855 conf_destroy_config();
2857 ast_channel_unregister(&record_tech);
2858 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
2860 res |= ast_manager_unregister("ConfbridgeList");
2861 res |= ast_manager_unregister("ConfbridgeListRooms");
2862 res |= ast_manager_unregister("ConfbridgeMute");
2863 res |= ast_manager_unregister("ConfbridgeUnmute");
2864 res |= ast_manager_unregister("ConfbridgeKick");
2865 res |= ast_manager_unregister("ConfbridgeUnlock");
2866 res |= ast_manager_unregister("ConfbridgeLock");
2867 res |= ast_manager_unregister("ConfbridgeStartRecord");
2868 res |= ast_manager_unregister("ConfbridgeStopRecord");
2874 * \brief Load the module
2876 * Module loading including tests for configuration or dependencies.
2877 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2878 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2879 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2880 * configuration file or other non-critical problem return
2881 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2883 static int load_module(void)
2887 if (conf_load_config(0)) {
2888 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
2889 return AST_MODULE_LOAD_DECLINE;
2891 if ((ast_custom_function_register(&confbridge_function))) {
2892 return AST_MODULE_LOAD_FAILURE;
2894 if ((ast_custom_function_register(&confbridge_info_function))) {
2895 return AST_MODULE_LOAD_FAILURE;
2897 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
2898 return AST_MODULE_LOAD_FAILURE;
2900 ast_format_cap_add_all(record_tech.capabilities);
2901 if (ast_channel_register(&record_tech)) {
2902 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
2903 return AST_MODULE_LOAD_FAILURE;
2905 /* Create a container to hold the conference bridges */
2906 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
2907 return AST_MODULE_LOAD_FAILURE;
2909 if (ast_register_application_xml(app, confbridge_exec)) {
2910 ao2_ref(conference_bridges, -1);
2911 return AST_MODULE_LOAD_FAILURE;
2914 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2915 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
2916 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
2917 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
2918 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
2919 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
2920 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
2921 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
2922 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
2923 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
2924 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
2926 return AST_MODULE_LOAD_FAILURE;
2929 return AST_MODULE_LOAD_SUCCESS;
2932 static int reload(void)
2934 return conf_load_config(1);
2937 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
2938 .load = load_module,
2939 .unload = unload_module,
2941 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,