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="true">
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(struct conference_bridge_user *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 conf_start_record(conference_bridge);
578 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
579 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
580 ao2_ref(conference_bridge, -1); /* error so remove ref */
587 static void send_conf_start_event(const char *conf_name)
590 <managerEventInstance>
591 <synopsis>Raised when a conference starts.</synopsis>
593 <parameter name="Conference">
594 <para>The name of the Confbridge conference.</para>
598 <ref type="managerEvent">ConfbridgeEnd</ref>
600 </managerEventInstance>
602 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
605 static void send_conf_end_event(const char *conf_name)
608 <managerEventInstance>
609 <synopsis>Raised when a conference ends.</synopsis>
611 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
614 <ref type="managerEvent">ConfbridgeStart</ref>
615 <ref type="application">ConfBridge</ref>
617 </managerEventInstance>
619 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
622 static void send_join_event(struct ast_channel *chan, const char *conf_name)
625 <managerEventInstance>
626 <synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
628 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
631 <ref type="managerEvent">ConfbridgeLeave</ref>
632 <ref type="application">ConfBridge</ref>
634 </managerEventInstance>
636 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
640 "CallerIDnum: %s\r\n"
641 "CallerIDname: %s\r\n",
642 ast_channel_name(chan),
643 ast_channel_uniqueid(chan),
645 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
646 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
650 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
653 <managerEventInstance>
654 <synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
656 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
659 <ref type="managerEvent">ConfbridgeJoin</ref>
661 </managerEventInstance>
663 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
667 "CallerIDnum: %s\r\n"
668 "CallerIDname: %s\r\n",
669 ast_channel_name(chan),
670 ast_channel_uniqueid(chan),
672 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
673 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
679 * \brief Complain if the given sound file does not exist.
681 * \param filename Sound file to check if exists.
683 * \retval non-zero if the file exists.
685 static int sound_file_exists(const char *filename)
687 if (ast_fileexists(filename, NULL, NULL)) {
690 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
695 * \brief Announce number of users in the conference bridge to the caller
697 * \param conference_bridge Conference bridge to peek at
698 * \param conference_bridge_user Optional Caller
700 * \note if caller is NULL, the announcment will be sent to all participants in the conference.
701 * \return Returns 0 on success, -1 if the user hung up
703 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
705 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
706 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
707 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
709 if (conference_bridge->activeusers <= 1) {
710 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
712 } else if (conference_bridge->activeusers == 2) {
713 if (conference_bridge_user) {
714 /* Eep, there is one other person */
715 if (ast_stream_and_wait(conference_bridge_user->chan,
721 play_sound_file(conference_bridge, only_one);
724 /* Alas multiple others in here */
725 if (conference_bridge_user) {
726 if (ast_stream_and_wait(conference_bridge_user->chan,
731 if (ast_say_number(conference_bridge_user->chan, conference_bridge->activeusers - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) {
734 if (ast_stream_and_wait(conference_bridge_user->chan,
739 } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
740 play_sound_file(conference_bridge, there_are);
741 play_sound_number(conference_bridge, conference_bridge->activeusers - 1);
742 play_sound_file(conference_bridge, other_in_party);
749 * \brief Play back an audio file to a channel
751 * \param cbu User to play audio prompt to
752 * \param filename Prompt to play
754 * \return Returns 0 on success, -1 if the user hung up
755 * \note Generally this should be called when the conference is unlocked to avoid blocking
756 * the entire conference while the sound is played. But don't unlock the conference bridge
757 * in the middle of a state transition.
759 static int play_prompt_to_user(struct conference_bridge_user *cbu, const char *filename)
761 return ast_stream_and_wait(cbu->chan, filename, "");
764 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
766 /* Right now, only marked users are automatically set as the single src of video.*/
771 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
773 struct conference_bridge_user *tmp_user = NULL;
774 ao2_lock(conference_bridge);
775 /* see if anyone is already the video src */
776 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
777 if (tmp_user->chan == chan) {
780 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
785 ao2_unlock(conference_bridge);
787 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
789 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
790 /* we joined and are video capable, we override anyone else that may have already been the video feed */
791 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
795 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
797 struct conference_bridge_user *tmp_user = NULL;
799 /* if this isn't a video source, nothing to update */
800 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
804 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
806 /* If in follow talker mode, make sure to restore this mode on the
807 * bridge when a source is removed. It is possible this channel was
808 * only set temporarily as a video source by an AMI or DTMF action. */
809 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
810 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
813 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
814 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
815 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
819 /* Make the next available marked user the video src. */
820 ao2_lock(conference_bridge);
821 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
822 if (tmp_user->chan == chan) {
825 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
826 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
830 ao2_unlock(conference_bridge);
834 * \brief Destroy a conference bridge
836 * \param obj The conference bridge object
838 * \return Returns nothing
840 static void destroy_conference_bridge(void *obj)
842 struct conference_bridge *conference_bridge = obj;
844 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
846 if (conference_bridge->playback_chan) {
847 struct ast_channel *underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
848 if (underlying_channel) {
849 ast_hangup(underlying_channel);
851 ast_hangup(conference_bridge->playback_chan);
852 conference_bridge->playback_chan = NULL;
855 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
856 if (conference_bridge->bridge) {
857 ast_bridge_destroy(conference_bridge->bridge);
858 conference_bridge->bridge = NULL;
861 conf_bridge_profile_destroy(&conference_bridge->b_profile);
862 ast_cond_destroy(&conference_bridge->record_cond);
863 ast_mutex_destroy(&conference_bridge->record_lock);
864 ast_mutex_destroy(&conference_bridge->playback_lock);
867 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
869 * \param cbu The conference bridge user that is joining
873 static int handle_conf_user_join(struct conference_bridge_user *cbu)
875 conference_event_fn handler;
876 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
877 handler = cbu->conference_bridge->state->join_marked;
878 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
879 handler = cbu->conference_bridge->state->join_waitmarked;
881 handler = cbu->conference_bridge->state->join_unmarked;
884 ast_assert(handler != NULL);
887 conf_invalid_event_fn(cbu);
896 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
898 * \param cbu The conference bridge user that is leaving
902 static int handle_conf_user_leave(struct conference_bridge_user *cbu)
904 conference_event_fn handler;
905 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
906 handler = cbu->conference_bridge->state->leave_marked;
907 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
908 handler = cbu->conference_bridge->state->leave_waitmarked;
910 handler = cbu->conference_bridge->state->leave_unmarked;
913 ast_assert(handler != NULL);
916 /* This should never happen. If it does, though, it is bad. The user will not have been removed
917 * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
918 * Shouldn't happen, though. */
919 conf_invalid_event_fn(cbu);
928 void conf_moh_stop(struct conference_bridge_user *user)
930 user->playing_moh = 0;
931 if (!user->suspended_moh) {
935 * Locking the ast_bridge here is the only way to hold off the
936 * call to ast_bridge_join() in confbridge_exec() from
937 * interfering with the bridge and MOH operations here.
939 ast_bridge_lock(user->conference_bridge->bridge);
942 * Temporarily suspend the user from the bridge so we have
943 * control to stop MOH if needed.
945 in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan);
946 ast_moh_stop(user->chan);
948 ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan);
951 ast_bridge_unlock(user->conference_bridge->bridge);
955 void conf_moh_start(struct conference_bridge_user *user)
957 user->playing_moh = 1;
958 if (!user->suspended_moh) {
962 * Locking the ast_bridge here is the only way to hold off the
963 * call to ast_bridge_join() in confbridge_exec() from
964 * interfering with the bridge and MOH operations here.
966 ast_bridge_lock(user->conference_bridge->bridge);
969 * Temporarily suspend the user from the bridge so we have
970 * control to start MOH if needed.
972 in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan);
973 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
975 ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan);
978 ast_bridge_unlock(user->conference_bridge->bridge);
984 * \brief Unsuspend MOH for the conference user.
986 * \param user Conference user to unsuspend MOH on.
990 static void conf_moh_unsuspend(struct conference_bridge_user *user)
992 ao2_lock(user->conference_bridge);
993 if (--user->suspended_moh == 0 && user->playing_moh) {
994 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
996 ao2_unlock(user->conference_bridge);
1001 * \brief Suspend MOH for the conference user.
1003 * \param user Conference user to suspend MOH on.
1007 static void conf_moh_suspend(struct conference_bridge_user *user)
1009 ao2_lock(user->conference_bridge);
1010 if (user->suspended_moh++ == 0 && user->playing_moh) {
1011 ast_moh_stop(user->chan);
1013 ao2_unlock(user->conference_bridge);
1016 int conf_handle_first_marked_common(struct conference_bridge_user *cbu)
1018 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))) {
1024 int conf_handle_inactive_waitmarked(struct conference_bridge_user *cbu)
1026 /* If we have not been quieted play back that they are waiting for the leader */
1027 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu,
1028 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, cbu->b_profile.sounds))) {
1029 /* user hungup while the sound was playing */
1035 int conf_handle_only_unmarked(struct conference_bridge_user *cbu)
1037 /* If audio prompts have not been quieted or this prompt quieted play it on out */
1038 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
1039 if (play_prompt_to_user(cbu,
1040 conf_get_sound(CONF_SOUND_ONLY_PERSON, cbu->b_profile.sounds))) {
1041 /* user hungup while the sound was playing */
1048 int conf_add_post_join_action(struct conference_bridge_user *cbu, int (*func)(struct conference_bridge_user *cbu))
1050 struct post_join_action *action;
1051 if (!(action = ast_calloc(1, sizeof(*action)))) {
1054 action->func = func;
1055 AST_LIST_INSERT_TAIL(&cbu->post_join_list, action, list);
1060 void conf_handle_first_join(struct conference_bridge *conference_bridge)
1062 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
1065 void conf_handle_second_active(struct conference_bridge *conference_bridge)
1067 /* If we are the second participant we may need to stop music on hold on the first */
1068 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->active_list);
1070 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD)) {
1071 conf_moh_stop(first_participant);
1073 if (!ast_test_flag(&first_participant->u_profile, USER_OPT_STARTMUTED)) {
1074 first_participant->features.mute = 0;
1078 void conf_ended(struct conference_bridge *conference_bridge)
1080 /* Called with a reference to conference_bridge */
1081 ao2_unlink(conference_bridges, conference_bridge);
1082 send_conf_end_event(conference_bridge->name);
1083 conf_stop_record_thread(conference_bridge);
1087 * \brief Join a conference bridge
1089 * \param name The conference name
1090 * \param conference_bridge_user Conference bridge user structure
1092 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1094 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
1096 struct conference_bridge *conference_bridge = NULL;
1097 struct post_join_action *action;
1098 struct conference_bridge tmp;
1099 int max_members_reached = 0;
1101 ast_copy_string(tmp.name, name, sizeof(tmp.name));
1103 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
1104 ao2_lock(conference_bridges);
1106 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
1108 /* Attempt to find an existing conference bridge */
1109 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1111 if (conference_bridge && conference_bridge->b_profile.max_members) {
1112 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->activeusers ? 0 : 1;
1115 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1116 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
1117 ao2_unlock(conference_bridges);
1118 ao2_ref(conference_bridge, -1);
1119 ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", name);
1120 ast_stream_and_wait(conference_bridge_user->chan,
1121 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
1126 /* If no conference bridge was found see if we can create one */
1127 if (!conference_bridge) {
1128 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1129 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
1130 ao2_unlock(conference_bridges);
1131 ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", name);
1135 /* Setup lock for playback channel */
1136 ast_mutex_init(&conference_bridge->playback_lock);
1138 /* Setup lock for the record channel */
1139 ast_mutex_init(&conference_bridge->record_lock);
1140 ast_cond_init(&conference_bridge->record_cond, NULL);
1142 /* Setup conference bridge parameters */
1143 conference_bridge->record_thread = AST_PTHREADT_NULL;
1144 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
1145 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
1147 /* Create an actual bridge that will do the audio mixing */
1148 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
1149 ao2_ref(conference_bridge, -1);
1150 conference_bridge = NULL;
1151 ao2_unlock(conference_bridges);
1152 ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", name);
1156 /* Set the internal sample rate on the bridge from the bridge profile */
1157 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
1158 /* Set the internal mixing interval on the bridge from the bridge profile */
1159 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
1161 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1162 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
1165 /* Link it into the conference bridges container */
1166 if (!ao2_link(conference_bridges, conference_bridge)) {
1167 ao2_ref(conference_bridge, -1);
1168 conference_bridge = NULL;
1169 ao2_unlock(conference_bridges);
1171 "Conference '%s' could not be added to the conferences list.\n", name);
1175 /* Set the initial state to EMPTY */
1176 conference_bridge->state = CONF_STATE_EMPTY;
1178 conference_bridge->record_state = CONF_RECORD_STOP;
1179 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
1180 ao2_lock(conference_bridge);
1181 start_conf_record_thread(conference_bridge);
1182 ao2_unlock(conference_bridge);
1185 send_conf_start_event(conference_bridge->name);
1186 ast_debug(1, "Created conference '%s' and linked to container.\n", name);
1189 ao2_unlock(conference_bridges);
1191 /* Setup conference bridge user parameters */
1192 conference_bridge_user->conference_bridge = conference_bridge;
1194 ao2_lock(conference_bridge);
1197 * Suspend any MOH until the user actually joins the bridge of
1198 * the conference. This way any pre-join file playback does not
1199 * need to worry about MOH.
1201 conference_bridge_user->suspended_moh = 1;
1203 if (handle_conf_user_join(conference_bridge_user)) {
1204 /* Invalid event, nothing was done, so we don't want to process a leave. */
1205 ao2_unlock(conference_bridge);
1206 ao2_ref(conference_bridge, -1);
1210 if (ast_check_hangup(conference_bridge_user->chan)) {
1211 ao2_unlock(conference_bridge);
1212 leave_conference(conference_bridge_user);
1216 ao2_unlock(conference_bridge);
1218 /* If an announcement is to be played play it */
1219 if (!ast_strlen_zero(conference_bridge_user->u_profile.announcement)) {
1220 if (play_prompt_to_user(conference_bridge_user,
1221 conference_bridge_user->u_profile.announcement)) {
1222 leave_conference(conference_bridge_user);
1227 /* Announce number of users if need be */
1228 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
1229 if (announce_user_count(conference_bridge, conference_bridge_user)) {
1230 leave_conference(conference_bridge_user);
1235 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
1236 (conference_bridge->activeusers > conference_bridge_user->u_profile.announce_user_count_all_after)) {
1240 * We have to autoservice the new user because he has not quite
1241 * joined the conference yet.
1243 ast_autoservice_start(conference_bridge_user->chan);
1244 user_count_res = announce_user_count(conference_bridge, NULL);
1245 ast_autoservice_stop(conference_bridge_user->chan);
1246 if (user_count_res) {
1247 leave_conference(conference_bridge_user);
1252 /* Handle post-join actions */
1253 while ((action = AST_LIST_REMOVE_HEAD(&conference_bridge_user->post_join_list, list))) {
1254 action->func(conference_bridge_user);
1258 return conference_bridge;
1262 * \brief Leave a conference
1264 * \param user The conference user
1266 static void leave_conference(struct conference_bridge_user *user)
1268 struct post_join_action *action;
1270 ao2_lock(user->conference_bridge);
1271 handle_conf_user_leave(user);
1272 ao2_unlock(user->conference_bridge);
1274 /* Discard any post-join actions */
1275 while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
1279 /* Done mucking with the conference, huzzah */
1280 ao2_ref(user->conference_bridge, -1);
1281 user->conference_bridge = NULL;
1286 * \brief allocates playback chan on a channel
1287 * \pre expects conference to be locked before calling this function
1289 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
1292 struct ast_format_cap *cap;
1293 struct ast_format tmpfmt;
1295 if (conference_bridge->playback_chan) {
1298 if (!(cap = ast_format_cap_alloc_nolock())) {
1301 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
1302 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
1303 cap = ast_format_cap_destroy(cap);
1306 cap = ast_format_cap_destroy(cap);
1308 ast_channel_internal_bridge_set(conference_bridge->playback_chan, conference_bridge->bridge);
1310 if (ast_call(conference_bridge->playback_chan, "", 0)) {
1311 ast_hangup(conference_bridge->playback_chan);
1312 conference_bridge->playback_chan = NULL;
1316 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
1320 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
1322 struct ast_channel *underlying_channel;
1324 /* Do not waste resources trying to play files that do not exist */
1325 if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) {
1329 ast_mutex_lock(&conference_bridge->playback_lock);
1330 if (!(conference_bridge->playback_chan)) {
1331 if (alloc_playback_chan(conference_bridge)) {
1332 ast_mutex_unlock(&conference_bridge->playback_lock);
1335 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1337 /* Channel was already available so we just need to add it back into the bridge */
1338 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1339 if (ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0)) {
1340 ast_mutex_unlock(&conference_bridge->playback_lock);
1345 /* The channel is all under our control, in goes the prompt */
1346 if (!ast_strlen_zero(filename)) {
1347 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
1348 } else if (say_number >= 0) {
1349 ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL);
1352 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge);
1353 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
1355 ast_mutex_unlock(&conference_bridge->playback_lock);
1360 int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
1362 return play_sound_helper(conference_bridge, filename, -1);
1366 * \brief Play number into the conference bridge
1368 * \param conference_bridge The conference bridge to say the number into
1369 * \param say_number number to say
1372 * \retval -1 failure
1374 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
1376 return play_sound_helper(conference_bridge, NULL, say_number);
1379 static void conf_handle_talker_destructor(void *pvt_data)
1384 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
1386 char *conf_name = pvt_data;
1389 switch (bridge_channel->state) {
1390 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
1393 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
1397 return; /* uhh this shouldn't happen, but bail if it does. */
1400 /* notify AMI someone is has either started or stopped talking */
1402 <managerEventInstance>
1403 <synopsis>Raised when a conference participant has started or stopped talking.</synopsis>
1405 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
1406 <parameter name="TalkingStatus">
1413 </managerEventInstance>
1415 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
1418 "Conference: %s\r\n"
1419 "TalkingStatus: %s\r\n",
1420 ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
1423 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
1425 char pin_guess[MAX_PIN+1] = { 0, };
1426 const char *pin = conference_bridge_user->u_profile.pin;
1427 char *tmp = pin_guess;
1429 unsigned int len = MAX_PIN ;
1431 /* give them three tries to get the pin right */
1432 for (i = 0; i < 3; i++) {
1433 if (ast_app_getdata(chan,
1434 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
1435 tmp, len, 0) >= 0) {
1436 if (!strcasecmp(pin, pin_guess)) {
1440 ast_streamfile(chan,
1441 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
1442 ast_channel_language(chan));
1443 res = ast_waitstream(chan, AST_DIGIT_ANY);
1445 /* Account for digit already read during ivalid pin playback
1446 * resetting pin buf. */
1448 pin_guess[1] = '\0';
1449 tmp = pin_guess + 1;
1452 /* reset pin buf as empty buffer. */
1460 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
1462 char destdir[PATH_MAX];
1466 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1468 if (ast_mkdir(destdir, 0777) != 0) {
1469 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1472 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1473 "%s/confbridge-name-%s-%s", destdir,
1474 conf_name, ast_channel_uniqueid(user->chan));
1476 res = ast_play_and_record(user->chan,
1478 user->name_rec_location,
1483 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1488 user->name_rec_location[0] = '\0';
1494 /*! \brief The ConfBridge application */
1495 static int confbridge_exec(struct ast_channel *chan, const char *data)
1497 int res = 0, volume_adjustments[2];
1500 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
1501 const char *u_profile_name = DEFAULT_USER_PROFILE;
1502 struct conference_bridge *conference_bridge = NULL;
1503 struct conference_bridge_user conference_bridge_user = {
1505 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1506 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1507 .tech_args.drop_silence = 0,
1509 AST_DECLARE_APP_ARGS(args,
1510 AST_APP_ARG(conf_name);
1511 AST_APP_ARG(b_profile_name);
1512 AST_APP_ARG(u_profile_name);
1513 AST_APP_ARG(menu_name);
1515 ast_bridge_features_init(&conference_bridge_user.features);
1517 if (ast_channel_state(chan) != AST_STATE_UP) {
1521 if (ast_strlen_zero(data)) {
1522 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1523 res = -1; /* invalid PIN */
1524 goto confbridge_cleanup;
1527 /* We need to make a copy of the input string if we are going to modify it! */
1528 parse = ast_strdupa(data);
1530 AST_STANDARD_APP_ARGS(args, parse);
1532 /* bridge profile name */
1533 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1534 b_profile_name = args.b_profile_name;
1536 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
1537 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
1539 goto confbridge_cleanup;
1542 /* user profile name */
1543 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1544 u_profile_name = args.u_profile_name;
1546 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
1547 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
1549 goto confbridge_cleanup;
1552 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
1554 /* ask for a PIN immediately after finding user profile. This has to be
1555 * prompted for requardless of quiet setting. */
1556 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
1557 if (conf_get_pin(chan, &conference_bridge_user)) {
1558 res = -1; /* invalid PIN */
1559 goto confbridge_cleanup;
1563 /* See if we need them to record a intro name */
1564 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
1565 conf_rec_name(&conference_bridge_user, args.conf_name);
1569 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
1570 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
1571 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
1572 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
1574 res = -1; /* invalid PIN */
1575 goto confbridge_cleanup;
1579 /* Set if DTMF should pass through for this user or not */
1580 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
1581 conference_bridge_user.features.dtmf_passthrough = 1;
1584 /* Set dsp threshold values if present */
1585 if (conference_bridge_user.u_profile.talking_threshold) {
1586 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
1588 if (conference_bridge_user.u_profile.silence_threshold) {
1589 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
1592 /* Set a talker indicate call back if talking detection is requested */
1593 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
1594 char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
1596 res = -1; /* invalid PIN */
1597 goto confbridge_cleanup;
1599 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
1600 conf_handle_talker_cb,
1601 conf_handle_talker_destructor,
1605 /* Look for a conference bridge matching the provided name */
1606 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
1607 res = -1; /* invalid PIN */
1608 goto confbridge_cleanup;
1611 /* Keep a copy of volume adjustments so we can restore them later if need be */
1612 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1613 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1615 /* If the caller should be joined already muted, make it so */
1616 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
1617 conference_bridge_user.features.mute = 1;
1620 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
1621 conference_bridge_user.tech_args.drop_silence = 1;
1624 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
1626 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1628 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1632 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
1634 /* Reduce background noise from each participant */
1635 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1636 ast_free(mod_speex);
1637 ast_func_write(chan, "DENOISE(rx)", "on");
1641 /* if this user has a intro, play it before entering */
1642 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1643 ast_autoservice_start(chan);
1644 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1645 play_sound_file(conference_bridge,
1646 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
1647 ast_autoservice_stop(chan);
1650 /* Play the Join sound to both the conference and the user entering. */
1652 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
1654 ast_stream_and_wait(chan, join_sound, "");
1655 ast_autoservice_start(chan);
1656 play_sound_file(conference_bridge, join_sound);
1657 ast_autoservice_stop(chan);
1660 /* See if we need to automatically set this user as a video source or not */
1661 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
1663 conf_moh_unsuspend(&conference_bridge_user);
1665 /* Join our conference bridge for real */
1666 send_join_event(conference_bridge_user.chan, conference_bridge->name);
1667 ast_bridge_join(conference_bridge->bridge,
1670 &conference_bridge_user.features,
1671 &conference_bridge_user.tech_args);
1672 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
1674 /* if we're shutting down, don't attempt to do further processing */
1675 if (ast_shutting_down()) {
1676 leave_conference(&conference_bridge_user);
1677 conference_bridge = NULL;
1678 goto confbridge_cleanup;
1681 /* If this user was a video source, we need to clean up and possibly pick a new source. */
1682 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
1684 /* if this user has a intro, play it when leaving */
1685 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1686 ast_autoservice_start(chan);
1687 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1688 play_sound_file(conference_bridge,
1689 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
1690 ast_autoservice_stop(chan);
1693 /* play the leave sound */
1695 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
1696 ast_autoservice_start(chan);
1697 play_sound_file(conference_bridge, leave_sound);
1698 ast_autoservice_stop(chan);
1701 /* Easy as pie, depart this channel from the conference bridge */
1702 leave_conference(&conference_bridge_user);
1703 conference_bridge = NULL;
1705 /* If the user was kicked from the conference play back the audio prompt for it */
1706 if (!quiet && conference_bridge_user.kicked) {
1707 res = ast_stream_and_wait(chan,
1708 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
1712 /* Restore volume adjustments to previous values in case they were changed */
1713 if (volume_adjustments[0]) {
1714 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1716 if (volume_adjustments[1]) {
1717 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1720 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1721 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
1725 ast_bridge_features_cleanup(&conference_bridge_user.features);
1726 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
1730 static int action_toggle_mute(struct conference_bridge *conference_bridge,
1731 struct conference_bridge_user *conference_bridge_user,
1732 struct ast_channel *chan)
1734 /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
1735 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
1736 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
1737 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));
1739 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
1740 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
1741 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
1745 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1747 struct conference_bridge_user *participant = NULL;
1748 const char *sound_to_play;
1750 ao2_lock(conference_bridge);
1752 /* If already muted, then unmute */
1753 conference_bridge->muted = conference_bridge->muted ? 0 : 1;
1754 sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
1755 conference_bridge_user->b_profile.sounds);
1757 AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) {
1758 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
1759 participant->features.mute = conference_bridge->muted;
1763 ao2_unlock(conference_bridge);
1765 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
1766 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
1768 /* Announce to the group that all participants are muted */
1769 ast_autoservice_start(conference_bridge_user->chan);
1770 play_sound_helper(conference_bridge, sound_to_play, 0);
1771 ast_autoservice_stop(conference_bridge_user->chan);
1776 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1778 char *file_copy = ast_strdupa(playback_file);
1781 while ((file = strsep(&file_copy, "&"))) {
1782 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1783 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1790 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
1791 struct conference_bridge_user *conference_bridge_user,
1792 struct ast_bridge_channel *bridge_channel,
1793 struct conf_menu *menu,
1794 const char *playback_file,
1795 const char *cur_dtmf,
1800 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1801 struct conf_menu_entry new_menu_entry = { { 0, }, };
1802 char *file_copy = ast_strdupa(playback_file);
1805 while ((file = strsep(&file_copy, "&"))) {
1806 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
1807 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1811 /* now wait for more digits. */
1812 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1813 /* streaming finished and no DTMF was entered */
1815 } else if (digit == -1) {
1819 break; /* dtmf was entered */
1823 /* streaming finished on all files and no DTMF was entered */
1826 ast_stopstream(bridge_channel->chan);
1828 /* If we get here, then DTMF has been entered, This means no
1829 * additional prompts should be played for this menu entry */
1832 /* If a digit was pressed during the payback, update
1833 * the dtmf string and look for a new menu entry in the
1835 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1836 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1837 dtmf[i] = cur_dtmf[i];
1839 dtmf[i] = (char) digit;
1845 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1846 * If this is the case, no new DTMF sequence should be looked for. */
1851 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1852 execute_menu_entry(conference_bridge,
1853 conference_bridge_user,
1855 &new_menu_entry, menu);
1856 conf_menu_entry_destroy(&new_menu_entry);
1861 static int action_kick_last(struct conference_bridge *conference_bridge,
1862 struct ast_bridge_channel *bridge_channel,
1863 struct conference_bridge_user *conference_bridge_user)
1865 struct conference_bridge_user *last_participant = NULL;
1866 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1869 ast_stream_and_wait(bridge_channel->chan,
1870 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1872 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1873 ast_channel_name(bridge_channel->chan),
1874 conference_bridge->name);
1878 ao2_lock(conference_bridge);
1879 if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user)
1880 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
1881 ao2_unlock(conference_bridge);
1882 ast_stream_and_wait(bridge_channel->chan,
1883 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1885 } else if (last_participant) {
1886 last_participant->kicked = 1;
1887 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
1888 ao2_unlock(conference_bridge);
1893 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1895 struct ast_pbx_args args;
1896 struct ast_pbx *pbx;
1902 memset(&args, 0, sizeof(args));
1903 args.no_hangup_chan = 1;
1905 ast_channel_lock(bridge_channel->chan);
1908 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
1909 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
1910 priority = ast_channel_priority(bridge_channel->chan);
1911 pbx = ast_channel_pbx(bridge_channel->chan);
1912 ast_channel_pbx_set(bridge_channel->chan, NULL);
1915 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
1916 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
1917 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
1919 ast_channel_unlock(bridge_channel->chan);
1922 res = ast_pbx_run_args(bridge_channel->chan, &args);
1925 ast_channel_lock(bridge_channel->chan);
1927 ast_channel_exten_set(bridge_channel->chan, exten);
1928 ast_channel_context_set(bridge_channel->chan, context);
1929 ast_channel_priority_set(bridge_channel->chan, priority);
1930 ast_channel_pbx_set(bridge_channel->chan, pbx);
1932 ast_channel_unlock(bridge_channel->chan);
1937 static int execute_menu_entry(struct conference_bridge *conference_bridge,
1938 struct conference_bridge_user *conference_bridge_user,
1939 struct ast_bridge_channel *bridge_channel,
1940 struct conf_menu_entry *menu_entry,
1941 struct conf_menu *menu)
1943 struct conf_menu_action *menu_action;
1944 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1945 int stop_prompts = 0;
1948 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1949 switch (menu_action->id) {
1950 case MENU_ACTION_TOGGLE_MUTE:
1951 res |= action_toggle_mute(conference_bridge,
1952 conference_bridge_user,
1953 bridge_channel->chan);
1955 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1959 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
1961 case MENU_ACTION_PARTICIPANT_COUNT:
1962 announce_user_count(conference_bridge, conference_bridge_user);
1964 case MENU_ACTION_PLAYBACK:
1965 if (!stop_prompts) {
1966 res |= action_playback(bridge_channel, menu_action->data.playback_file);
1969 case MENU_ACTION_RESET_LISTENING:
1970 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
1972 case MENU_ACTION_RESET_TALKING:
1973 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
1975 case MENU_ACTION_INCREASE_LISTENING:
1976 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1977 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
1979 case MENU_ACTION_DECREASE_LISTENING:
1980 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1981 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
1983 case MENU_ACTION_INCREASE_TALKING:
1984 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1985 AST_AUDIOHOOK_DIRECTION_READ, 1);
1987 case MENU_ACTION_DECREASE_TALKING:
1988 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1989 AST_AUDIOHOOK_DIRECTION_READ, -1);
1991 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1992 if (!(stop_prompts)) {
1993 res |= action_playback_and_continue(conference_bridge,
1994 conference_bridge_user,
1997 menu_action->data.playback_file,
2002 case MENU_ACTION_DIALPLAN_EXEC:
2003 res |= action_dialplan_exec(bridge_channel, menu_action);
2005 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
2009 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
2010 res |= ast_stream_and_wait(bridge_channel->chan,
2011 (conference_bridge->locked ?
2012 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
2013 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
2017 case MENU_ACTION_ADMIN_KICK_LAST:
2018 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
2020 case MENU_ACTION_LEAVE:
2021 ao2_lock(conference_bridge);
2022 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
2023 ao2_unlock(conference_bridge);
2025 case MENU_ACTION_NOOP:
2027 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
2028 ao2_lock(conference_bridge);
2029 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
2030 ao2_unlock(conference_bridge);
2032 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
2033 handle_video_on_exit(conference_bridge, bridge_channel->chan);
2040 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
2041 struct conference_bridge_user *conference_bridge_user,
2042 struct conf_menu_entry *menu_entry,
2043 struct conf_menu *menu)
2045 /* See if music on hold is playing */
2046 conf_moh_suspend(conference_bridge_user);
2048 /* execute the list of actions associated with this menu entry */
2049 execute_menu_entry(conference_bridge_user->conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
2051 /* See if music on hold needs to be started back up again */
2052 conf_moh_unsuspend(conference_bridge_user);
2057 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
2060 struct conference_bridge *bridge = NULL;
2062 int wordlen = strlen(word);
2063 struct ao2_iterator i;
2065 i = ao2_iterator_init(conference_bridges, 0);
2066 while ((bridge = ao2_iterator_next(&i))) {
2067 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
2068 res = ast_strdup(bridge->name);
2069 ao2_ref(bridge, -1);
2072 ao2_ref(bridge, -1);
2074 ao2_iterator_destroy(&i);
2079 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2081 struct conference_bridge *bridge = NULL;
2082 struct conference_bridge tmp;
2083 struct conference_bridge_user *participant = NULL;
2087 e->command = "confbridge kick";
2089 "Usage: confbridge kick <conference> <channel>\n"
2090 " Kicks a channel out of the conference bridge.\n";
2094 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2098 return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
2105 return CLI_SHOWUSAGE;
2108 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2109 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2111 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2115 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2116 if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
2121 ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
2122 participant->kicked = 1;
2123 ast_bridge_remove(bridge->bridge, participant->chan);
2126 ao2_ref(bridge, -1);
2130 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct conference_bridge_user *participant)
2132 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
2133 ast_cli(a->fd, "%-17s", participant->u_profile.name);
2134 ast_cli(a->fd, "%-17s", participant->b_profile.name);
2135 ast_cli(a->fd, "%-17s", participant->menu_name);
2136 ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
2137 ast_cli(a->fd, "\n");
2140 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2142 struct ao2_iterator i;
2143 struct conference_bridge *bridge = NULL;
2144 struct conference_bridge tmp;
2145 struct conference_bridge_user *participant = NULL;
2149 e->command = "confbridge list";
2151 "Usage: confbridge list [<name>]\n"
2152 " Lists all currently active conference bridges.\n";
2156 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2162 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
2163 ast_cli(a->fd, "================================ ====== ====== ========\n");
2164 i = ao2_iterator_init(conference_bridges, 0);
2165 while ((bridge = ao2_iterator_next(&i))) {
2166 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers + bridge->waitingusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
2167 ao2_ref(bridge, -1);
2169 ao2_iterator_destroy(&i);
2174 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2175 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2177 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2180 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
2181 ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
2183 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2184 handle_cli_confbridge_list_item(a, participant);
2186 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
2187 handle_cli_confbridge_list_item(a, participant);
2190 ao2_ref(bridge, -1);
2194 return CLI_SHOWUSAGE;
2198 * \brief finds a conference by name and locks/unlocks.
2201 * \retval -1 conference not found
2203 static int generic_lock_unlock_helper(int lock, const char *conference)
2205 struct conference_bridge *bridge = NULL;
2206 struct conference_bridge tmp;
2209 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2210 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2215 bridge->locked = lock;
2216 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
2218 ao2_ref(bridge, -1);
2224 * \brief finds a conference user by channel name and mutes/unmutes them.
2227 * \retval -1 conference not found
2228 * \retval -2 user not found
2230 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
2232 struct conference_bridge *bridge = NULL;
2233 struct conference_bridge tmp;
2234 struct conference_bridge_user *participant = NULL;
2236 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2237 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2242 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2243 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
2248 participant->features.mute = mute;
2249 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));
2254 ao2_ref(bridge, -1);
2259 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2261 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2264 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2266 } else if (res == -2) {
2267 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2270 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2274 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2278 e->command = "confbridge mute";
2280 "Usage: confbridge mute <conference> <channel>\n";
2284 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2289 return CLI_SHOWUSAGE;
2292 cli_mute_unmute_helper(1, a);
2297 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2301 e->command = "confbridge unmute";
2303 "Usage: confbridge unmute <conference> <channel>\n";
2307 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2312 return CLI_SHOWUSAGE;
2315 cli_mute_unmute_helper(0, a);
2320 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2324 e->command = "confbridge lock";
2326 "Usage: confbridge lock <conference>\n";
2330 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2335 return CLI_SHOWUSAGE;
2337 if (generic_lock_unlock_helper(1, a->argv[2])) {
2338 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2340 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2345 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2349 e->command = "confbridge unlock";
2351 "Usage: confbridge unlock <conference>\n";
2355 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2360 return CLI_SHOWUSAGE;
2362 if (generic_lock_unlock_helper(0, a->argv[2])) {
2363 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2365 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2370 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2372 const char *rec_file = NULL;
2373 struct conference_bridge *bridge = NULL;
2374 struct conference_bridge tmp;
2378 e->command = "confbridge record start";
2380 "Usage: confbridge record start <conference> <file>\n"
2381 " <file> is optional, Otherwise the bridge profile\n"
2382 " record file will be used. If the bridge profile\n"
2383 " has no record file specified, a file will automatically\n"
2384 " be generated in the monitor directory\n";
2388 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2393 return CLI_SHOWUSAGE;
2396 rec_file = a->argv[4];
2399 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2400 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2402 ast_cli(a->fd, "Conference not found.\n");
2406 if (conf_is_recording(bridge)) {
2407 ast_cli(a->fd, "Conference is already being recorded.\n");
2409 ao2_ref(bridge, -1);
2412 if (!ast_strlen_zero(rec_file)) {
2413 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2416 if (start_conf_record_thread(bridge)) {
2417 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2419 ao2_ref(bridge, -1);
2424 ast_cli(a->fd, "Recording started\n");
2425 ao2_ref(bridge, -1);
2429 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2431 struct conference_bridge *bridge = NULL;
2432 struct conference_bridge tmp;
2437 e->command = "confbridge record stop";
2439 "Usage: confbridge record stop <conference>\n";
2443 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2448 return CLI_SHOWUSAGE;
2451 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2452 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2454 ast_cli(a->fd, "Conference not found.\n");
2458 ret = conf_stop_record(bridge);
2460 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
2461 ao2_ref(bridge, -1);
2465 static struct ast_cli_entry cli_confbridge[] = {
2466 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2467 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2468 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2469 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
2470 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2471 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2472 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2473 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2475 static struct ast_custom_function confbridge_function = {
2476 .name = "CONFBRIDGE",
2477 .write = func_confbridge_helper,
2480 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2481 static struct ast_custom_function confbridge_info_function = {
2482 .name = "CONFBRIDGE_INFO",
2483 .read = func_confbridge_info,
2486 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct conference_bridge *bridge, struct conference_bridge_user *participant)
2489 "Event: ConfbridgeList\r\n"
2491 "Conference: %s\r\n"
2492 "CallerIDNum: %s\r\n"
2493 "CallerIDName: %s\r\n"
2496 "MarkedUser: %s\r\n"
2500 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
2501 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
2502 ast_channel_name(participant->chan),
2503 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2504 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
2507 static int action_confbridgelist(struct mansession *s, const struct message *m)
2509 const char *actionid = astman_get_header(m, "ActionID");
2510 const char *conference = astman_get_header(m, "Conference");
2511 struct conference_bridge_user *participant = NULL;
2512 struct conference_bridge *bridge = NULL;
2513 struct conference_bridge tmp;
2514 char id_text[80] = "";
2517 if (!ast_strlen_zero(actionid)) {
2518 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2520 if (ast_strlen_zero(conference)) {
2521 astman_send_error(s, m, "No Conference name provided.");
2524 if (!ao2_container_count(conference_bridges)) {
2525 astman_send_error(s, m, "No active conferences.");
2528 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2529 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2531 astman_send_error(s, m, "No Conference by that name found.");
2535 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2538 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2540 action_confbridgelist_item(s, id_text, bridge, participant);
2542 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
2544 action_confbridgelist_item(s, id_text, bridge, participant);
2547 ao2_ref(bridge, -1);
2550 "Event: ConfbridgeListComplete\r\n"
2551 "EventList: Complete\r\n"
2554 "\r\n", total, id_text);
2559 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2561 const char *actionid = astman_get_header(m, "ActionID");
2562 struct conference_bridge *bridge = NULL;
2563 struct ao2_iterator i;
2564 char id_text[512] = "";
2567 if (!ast_strlen_zero(actionid)) {
2568 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2571 if (!ao2_container_count(conference_bridges)) {
2572 astman_send_error(s, m, "No active conferences.");
2576 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2578 /* Traverse the conference list */
2579 i = ao2_iterator_init(conference_bridges, 0);
2580 while ((bridge = ao2_iterator_next(&i))) {
2585 "Event: ConfbridgeListRooms\r\n"
2587 "Conference: %s\r\n"
2594 bridge->activeusers + bridge->waitingusers,
2595 bridge->markedusers,
2596 bridge->locked ? "Yes" : "No");
2599 ao2_ref(bridge, -1);
2601 ao2_iterator_destroy(&i);
2603 /* Send final confirmation */
2605 "Event: ConfbridgeListRoomsComplete\r\n"
2606 "EventList: Complete\r\n"
2609 "\r\n", totalitems, id_text);
2613 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2615 const char *conference = astman_get_header(m, "Conference");
2616 const char *channel = astman_get_header(m, "Channel");
2619 if (ast_strlen_zero(conference)) {
2620 astman_send_error(s, m, "No Conference name provided.");
2623 if (ast_strlen_zero(channel)) {
2624 astman_send_error(s, m, "No channel name provided.");
2627 if (!ao2_container_count(conference_bridges)) {
2628 astman_send_error(s, m, "No active conferences.");
2632 res = generic_mute_unmute_helper(mute, conference, channel);
2635 astman_send_error(s, m, "No Conference by that name found.");
2637 } else if (res == -2) {
2638 astman_send_error(s, m, "No Channel by that name found in Conference.");
2642 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2646 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2648 return action_mute_unmute_helper(s, m, 0);
2650 static int action_confbridgemute(struct mansession *s, const struct message *m)
2652 return action_mute_unmute_helper(s, m, 1);
2655 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2657 const char *conference = astman_get_header(m, "Conference");
2660 if (ast_strlen_zero(conference)) {
2661 astman_send_error(s, m, "No Conference name provided.");
2664 if (!ao2_container_count(conference_bridges)) {
2665 astman_send_error(s, m, "No active conferences.");
2668 if ((res = generic_lock_unlock_helper(lock, conference))) {
2669 astman_send_error(s, m, "No Conference by that name found.");
2672 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2675 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2677 return action_lock_unlock_helper(s, m, 0);
2679 static int action_confbridgelock(struct mansession *s, const struct message *m)
2681 return action_lock_unlock_helper(s, m, 1);
2684 static int action_confbridgekick(struct mansession *s, const struct message *m)
2686 const char *conference = astman_get_header(m, "Conference");
2687 const char *channel = astman_get_header(m, "Channel");
2688 struct conference_bridge_user *participant = NULL;
2689 struct conference_bridge *bridge = NULL;
2690 struct conference_bridge tmp;
2693 if (ast_strlen_zero(conference)) {
2694 astman_send_error(s, m, "No Conference name provided.");
2697 if (!ao2_container_count(conference_bridges)) {
2698 astman_send_error(s, m, "No active conferences.");
2701 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2702 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2704 astman_send_error(s, m, "No Conference by that name found.");
2709 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2710 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
2711 participant->kicked = 1;
2712 ast_bridge_remove(bridge->bridge, participant->chan);
2718 ao2_ref(bridge, -1);
2721 astman_send_ack(s, m, "User kicked");
2723 astman_send_error(s, m, "No Channel by that name found in Conference.");
2728 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2730 const char *conference = astman_get_header(m, "Conference");
2731 const char *recordfile = astman_get_header(m, "RecordFile");
2732 struct conference_bridge *bridge = NULL;
2733 struct conference_bridge tmp;
2735 if (ast_strlen_zero(conference)) {
2736 astman_send_error(s, m, "No Conference name provided.");
2739 if (!ao2_container_count(conference_bridges)) {
2740 astman_send_error(s, m, "No active conferences.");
2744 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2745 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2747 astman_send_error(s, m, "No Conference by that name found.");
2752 if (conf_is_recording(bridge)) {
2753 astman_send_error(s, m, "Conference is already being recorded.");
2755 ao2_ref(bridge, -1);
2759 if (!ast_strlen_zero(recordfile)) {
2760 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2763 if (start_conf_record_thread(bridge)) {
2764 astman_send_error(s, m, "Internal error starting conference recording.");
2766 ao2_ref(bridge, -1);
2771 ao2_ref(bridge, -1);
2772 astman_send_ack(s, m, "Conference Recording Started.");
2775 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2777 const char *conference = astman_get_header(m, "Conference");
2778 struct conference_bridge *bridge = NULL;
2779 struct conference_bridge tmp;
2781 if (ast_strlen_zero(conference)) {
2782 astman_send_error(s, m, "No Conference name provided.");
2785 if (!ao2_container_count(conference_bridges)) {
2786 astman_send_error(s, m, "No active conferences.");
2790 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2791 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2793 astman_send_error(s, m, "No Conference by that name found.");
2798 if (conf_stop_record(bridge)) {
2800 astman_send_error(s, m, "Internal error while stopping recording.");
2801 ao2_ref(bridge, -1);
2806 ao2_ref(bridge, -1);
2807 astman_send_ack(s, m, "Conference Recording Stopped.");
2811 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2813 const char *conference = astman_get_header(m, "Conference");
2814 const char *channel = astman_get_header(m, "Channel");
2815 struct conference_bridge_user *participant = NULL;
2816 struct conference_bridge *bridge = NULL;
2817 struct conference_bridge tmp;
2819 if (ast_strlen_zero(conference)) {
2820 astman_send_error(s, m, "No Conference name provided.");
2823 if (ast_strlen_zero(channel)) {
2824 astman_send_error(s, m, "No channel name provided.");
2827 if (!ao2_container_count(conference_bridges)) {
2828 astman_send_error(s, m, "No active conferences.");
2832 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2833 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2835 astman_send_error(s, m, "No Conference by that name found.");
2839 /* find channel and set as video src. */
2841 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2842 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
2843 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2848 ao2_ref(bridge, -1);
2850 /* do not access participant after bridge unlock. We are just
2851 * using this check to see if it was found or not */
2853 astman_send_error(s, m, "No channel by that name found in conference.");
2856 astman_send_ack(s, m, "Conference single video source set.");
2860 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2863 struct conference_bridge *bridge = NULL;
2864 struct conference_bridge_user *participant = NULL;
2865 struct conference_bridge tmp;
2867 AST_DECLARE_APP_ARGS(args,
2869 AST_APP_ARG(confno);
2872 /* parse all the required arguments and make sure they exist. */
2873 if (ast_strlen_zero(data)) {
2876 parse = ast_strdupa(data);
2877 AST_STANDARD_APP_ARGS(args, parse);
2878 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
2881 if (!ao2_container_count(conference_bridges)) {
2882 snprintf(buf, len, "0");
2885 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
2886 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2888 snprintf(buf, len, "0");
2892 /* get the correct count for the type requested */
2894 if (!strncasecmp(args.type, "parties", 7)) {
2895 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2898 } else if (!strncasecmp(args.type, "admins", 6)) {
2899 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2900 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
2904 } else if (!strncasecmp(args.type, "marked", 6)) {
2905 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2906 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
2910 } else if (!strncasecmp(args.type, "locked", 6)) {
2911 count = bridge->locked;
2913 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
2914 "parties, admins, marked, or locked.\n", args.type);
2916 snprintf(buf, len, "%d", count);
2918 ao2_ref(bridge, -1);
2922 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2924 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
2925 conference_bridge->activeusers++;
2928 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2930 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
2931 conference_bridge->activeusers++;
2932 conference_bridge->markedusers++;
2935 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2937 AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
2938 conference_bridge->waitingusers++;
2941 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2943 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
2944 conference_bridge->activeusers--;
2947 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2949 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
2950 conference_bridge->activeusers--;
2951 conference_bridge->markedusers--;
2954 void conf_mute_only_active(struct conference_bridge *conference_bridge)
2956 struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
2958 /* Turn on MOH/mute if the single participant is set up for it */
2959 if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
2960 only_participant->features.mute = 1;
2961 conf_moh_start(only_participant);
2965 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
2967 AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
2968 conference_bridge->waitingusers--;
2971 /*! \brief Called when module is being unloaded */
2972 static int unload_module(void)
2974 int res = ast_unregister_application(app);
2976 ast_custom_function_unregister(&confbridge_function);
2977 ast_custom_function_unregister(&confbridge_info_function);
2979 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2981 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
2982 ao2_ref(conference_bridges, -1);
2984 conf_destroy_config();
2986 ast_channel_unregister(&record_tech);
2987 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
2989 res |= ast_manager_unregister("ConfbridgeList");
2990 res |= ast_manager_unregister("ConfbridgeListRooms");
2991 res |= ast_manager_unregister("ConfbridgeMute");
2992 res |= ast_manager_unregister("ConfbridgeUnmute");
2993 res |= ast_manager_unregister("ConfbridgeKick");
2994 res |= ast_manager_unregister("ConfbridgeUnlock");
2995 res |= ast_manager_unregister("ConfbridgeLock");
2996 res |= ast_manager_unregister("ConfbridgeStartRecord");
2997 res |= ast_manager_unregister("ConfbridgeStopRecord");
3003 * \brief Load the module
3005 * Module loading including tests for configuration or dependencies.
3006 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
3007 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
3008 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
3009 * configuration file or other non-critical problem return
3010 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
3012 static int load_module(void)
3016 if (conf_load_config(0)) {
3017 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
3018 return AST_MODULE_LOAD_DECLINE;
3020 if ((ast_custom_function_register(&confbridge_function))) {
3021 return AST_MODULE_LOAD_FAILURE;
3023 if ((ast_custom_function_register(&confbridge_info_function))) {
3024 return AST_MODULE_LOAD_FAILURE;
3026 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
3027 return AST_MODULE_LOAD_FAILURE;
3029 ast_format_cap_add_all(record_tech.capabilities);
3030 if (ast_channel_register(&record_tech)) {
3031 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
3032 return AST_MODULE_LOAD_FAILURE;
3034 /* Create a container to hold the conference bridges */
3035 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
3036 return AST_MODULE_LOAD_FAILURE;
3038 if (ast_register_application_xml(app, confbridge_exec)) {
3039 ao2_ref(conference_bridges, -1);
3040 return AST_MODULE_LOAD_FAILURE;
3043 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
3044 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
3045 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
3046 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
3047 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
3048 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
3049 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
3050 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
3051 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
3052 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
3053 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
3055 return AST_MODULE_LOAD_FAILURE;
3058 return AST_MODULE_LOAD_SUCCESS;
3061 static int reload(void)
3063 return conf_load_config(1);
3066 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
3067 .load = load_module,
3068 .unload = unload_module,
3070 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,