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
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41 #include "asterisk/cli.h"
42 #include "asterisk/file.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/lock.h"
48 #include "asterisk/bridging.h"
49 #include "asterisk/musiconhold.h"
50 #include "asterisk/say.h"
51 #include "asterisk/audiohook.h"
52 #include "asterisk/astobj2.h"
53 #include "confbridge/include/confbridge.h"
54 #include "asterisk/paths.h"
55 #include "asterisk/manager.h"
58 <application name="ConfBridge" language="en_US">
60 Conference bridge application.
63 <parameter name="confno">
64 <para>The conference number</para>
66 <parameter name="bridge_profile">
67 <para>The bridge profile name from confbridge.conf. When left blank, a dynamically built bridge profile created by the CONFBRIDGE dialplan function is searched for on the channel and used. If no dynamic profile is present, the 'default_bridge' profile found in confbridge.conf is used. </para>
68 <para>It is important to note that while user profiles may be unique for each participant, mixing bridge profiles on a single conference is _NOT_ recommended and will produce undefined results.</para>
70 <parameter name="user_profile">
71 <para>The user profile name from confbridge.conf. When left blank, a dynamically built user profile created by the CONFBRIDGE dialplan function is searched for on the channel and used. If no dynamic profile is present, the 'default_user' profile found in confbridge.conf is used.</para>
73 <parameter name="menu">
74 <para>The name of the DTMF menu in confbridge.conf to be applied to this channel. No menu is applied by default if this option is left blank.</para>
78 <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option.</para>
81 <ref type="application">ConfBridge</ref>
82 <ref type="function">CONFBRIDGE</ref>
83 <ref type="function">CONFBRIDGE_INFO</ref>
86 <function name="CONFBRIDGE" language="en_US">
88 Set a custom dynamic bridge and user profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
91 <parameter name="type" required="true">
92 <para>Type refers to which type of profile the option belongs too. Type can be <literal>bridge</literal> or <literal>user</literal>.</para>
94 <parameter name="option" required="true">
95 <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel.</para>
99 <para>---- Example 1 ----</para>
100 <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
101 <para>exten => 1,1,Answer() </para>
102 <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
103 <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
104 <para>exten => 1,n,ConfBridge(1) </para>
105 <para>---- Example 2 ----</para>
106 <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>
107 <para>exten => 1,1,Answer() </para>
108 <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
109 <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
110 <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
111 <para>exten => 1,n,ConfBridge(1)</para>
114 <function name="CONFBRIDGE_INFO" language="en_US">
116 Get information about a ConfBridge conference.
119 <parameter name="type" required="true">
120 <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
122 <parameter name="conf" required="true">
123 <para>Conf refers to the name of the conference being referenced.</para>
127 <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>
130 <manager name="ConfbridgeList" language="en_US">
132 List participants in a conference.
135 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
136 <parameter name="Conference" required="false">
137 <para>Conference number.</para>
141 <para>Lists all users in a particular ConfBridge conference.
142 ConfbridgeList will follow as separate events, followed by a final event called
143 ConfbridgeListComplete.</para>
146 <manager name="ConfbridgeListRooms" language="en_US">
148 List active conferences.
151 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
154 <para>Lists data about all active conferences.
155 ConfbridgeListRooms will follow as separate events, followed by a final event called
156 ConfbridgeListRoomsComplete.</para>
159 <manager name="ConfbridgeMute" language="en_US">
161 Mute a Confbridge user.
164 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
165 <parameter name="Conference" required="true" />
166 <parameter name="Channel" required="true" />
171 <manager name="ConfbridgeUnmute" language="en_US">
173 Unmute a Confbridge user.
176 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
177 <parameter name="Conference" required="true" />
178 <parameter name="Channel" required="true" />
183 <manager name="ConfbridgeKick" language="en_US">
185 Kick a Confbridge user.
188 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
189 <parameter name="Conference" required="true" />
190 <parameter name="Channel" required="true" />
195 <manager name="ConfbridgeLock" language="en_US">
197 Lock a Confbridge conference.
200 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
201 <parameter name="Conference" required="true" />
206 <manager name="ConfbridgeUnlock" language="en_US">
208 Unlock a Confbridge conference.
211 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
212 <parameter name="Conference" required="true" />
217 <manager name="ConfbridgeStartRecord" language="en_US">
219 Start recording a Confbridge conference.
222 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
223 <parameter name="Conference" required="true" />
224 <parameter name="RecordFile" required="false" />
227 <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>
230 <manager name="ConfbridgeStopRecord" language="en_US">
232 Stop recording a Confbridge conference.
235 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
236 <parameter name="Conference" required="true" />
241 <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
243 Set a conference user as the single video source distributed to all other participants.
246 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
247 <parameter name="Conference" required="true" />
248 <parameter name="Channel" required="true" />
257 * \par Playing back a file to a channel in a conference
258 * You might notice in this application that while playing a sound file
259 * to a channel the actual conference bridge lock is not held. This is done so
260 * that other channels are not blocked from interacting with the conference bridge.
261 * Unfortunately because of this it is possible for things to change after the sound file
262 * is done being played. Data must therefore be checked after reacquiring the conference
263 * bridge lock if it is important.
266 static const char app[] = "ConfBridge";
268 /* Number of buckets our conference bridges container can have */
269 #define CONFERENCE_BRIDGE_BUCKETS 53
271 /*! \brief Container to hold all conference bridges in progress */
272 static struct ao2_container *conference_bridges;
274 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
275 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number);
276 static int execute_menu_entry(struct conference_bridge *conference_bridge,
277 struct conference_bridge_user *conference_bridge_user,
278 struct ast_bridge_channel *bridge_channel,
279 struct conf_menu_entry *menu_entry,
280 struct conf_menu *menu);
282 /*! \brief Hashing function used for conference bridges container */
283 static int conference_bridge_hash_cb(const void *obj, const int flags)
285 const struct conference_bridge *conference_bridge = obj;
286 return ast_str_case_hash(conference_bridge->name);
289 /*! \brief Comparison function used for conference bridges container */
290 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
292 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
293 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
296 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
299 case CONF_SOUND_HAS_JOINED:
300 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
301 case CONF_SOUND_HAS_LEFT:
302 return S_OR(custom_sounds->hasleft, "conf-hasleft");
303 case CONF_SOUND_KICKED:
304 return S_OR(custom_sounds->kicked, "conf-kicked");
305 case CONF_SOUND_MUTED:
306 return S_OR(custom_sounds->muted, "conf-muted");
307 case CONF_SOUND_UNMUTED:
308 return S_OR(custom_sounds->unmuted, "conf-unmuted");
309 case CONF_SOUND_ONLY_ONE:
310 return S_OR(custom_sounds->onlyone, "conf-onlyone");
311 case CONF_SOUND_THERE_ARE:
312 return S_OR(custom_sounds->thereare, "conf-thereare");
313 case CONF_SOUND_OTHER_IN_PARTY:
314 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
315 case CONF_SOUND_PLACE_IN_CONF:
316 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
317 case CONF_SOUND_WAIT_FOR_LEADER:
318 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
319 case CONF_SOUND_LEADER_HAS_LEFT:
320 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
321 case CONF_SOUND_GET_PIN:
322 return S_OR(custom_sounds->getpin, "conf-getpin");
323 case CONF_SOUND_INVALID_PIN:
324 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
325 case CONF_SOUND_ONLY_PERSON:
326 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
327 case CONF_SOUND_LOCKED:
328 return S_OR(custom_sounds->locked, "conf-locked");
329 case CONF_SOUND_LOCKED_NOW:
330 return S_OR(custom_sounds->lockednow, "conf-lockednow");
331 case CONF_SOUND_UNLOCKED_NOW:
332 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
333 case CONF_SOUND_ERROR_MENU:
334 return S_OR(custom_sounds->errormenu, "conf-errormenu");
335 case CONF_SOUND_JOIN:
336 return S_OR(custom_sounds->join, "confbridge-join");
337 case CONF_SOUND_LEAVE:
338 return S_OR(custom_sounds->leave, "confbridge-leave");
344 static struct ast_frame *rec_read(struct ast_channel *ast)
346 return &ast_null_frame;
348 static int rec_write(struct ast_channel *ast, struct ast_frame *f)
352 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause);
353 static struct ast_channel_tech record_tech = {
354 .type = "ConfBridgeRec",
355 .description = "Conference Bridge Recording Channel",
356 .requester = rec_request,
360 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int *cause)
362 struct ast_channel *tmp;
363 struct ast_format fmt;
364 const char *conf_name = data;
365 if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
366 "ConfBridgeRecorder/conf-%s-uid-%d",
368 (int) ast_random()))) {
371 ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
372 tmp->tech = &record_tech;
373 ast_format_cap_add_all(tmp->nativeformats);
374 ast_format_copy(&tmp->writeformat, &fmt);
375 ast_format_copy(&tmp->rawwriteformat, &fmt);
376 ast_format_copy(&tmp->readformat, &fmt);
377 ast_format_copy(&tmp->rawreadformat, &fmt);
381 static void *record_thread(void *obj)
383 struct conference_bridge *conference_bridge = obj;
384 struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
385 struct ast_channel *chan;
386 struct ast_str *filename = ast_str_alloca(PATH_MAX);
389 ao2_ref(conference_bridge, -1);
393 ao2_lock(conference_bridge);
394 if (!(conference_bridge->record_chan)) {
395 conference_bridge->record_thread = AST_PTHREADT_NULL;
396 ao2_unlock(conference_bridge);
397 ao2_ref(conference_bridge, -1);
400 chan = ast_channel_ref(conference_bridge->record_chan);
402 if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
403 ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
407 ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
408 conference_bridge->name,
411 ao2_unlock(conference_bridge);
414 pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
415 ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
417 ao2_lock(conference_bridge);
418 conference_bridge->record_thread = AST_PTHREADT_NULL;
419 ao2_unlock(conference_bridge);
421 ast_hangup(chan); /* This will eat this threads reference to the channel as well */
422 ao2_ref(conference_bridge, -1);
428 * \brief Returns whether or not conference is being recorded.
429 * \retval 1, conference is recording.
430 * \retval 0, conference is NOT recording.
432 static int conf_is_recording(struct conference_bridge *conference_bridge)
435 ao2_lock(conference_bridge);
436 if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
439 ao2_unlock(conference_bridge);
445 * \brief Stops the confbridge recording thread.
447 * \note do not call this function with any locks
449 static int conf_stop_record(struct conference_bridge *conference_bridge)
451 ao2_lock(conference_bridge);
453 if (conference_bridge->record_thread != AST_PTHREADT_NULL) {
454 struct ast_channel *chan = ast_channel_ref(conference_bridge->record_chan);
455 pthread_t thread = conference_bridge->record_thread;
456 ao2_unlock(conference_bridge);
458 ast_bridge_remove(conference_bridge->bridge, chan);
459 ast_queue_frame(chan, &ast_null_frame);
461 chan = ast_channel_unref(chan);
462 pthread_join(thread, NULL);
464 ao2_lock(conference_bridge);
467 /* this is the reference given to the channel during the channel alloc */
468 if (conference_bridge->record_chan) {
469 conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan);
472 ao2_unlock(conference_bridge);
476 static int conf_start_record(struct conference_bridge *conference_bridge)
478 struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
479 struct ast_format tmpfmt;
482 ao2_lock(conference_bridge);
483 if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
484 ao2_unlock(conference_bridge);
485 return -1; /* already recording */
488 ao2_unlock(conference_bridge);
491 if (!pbx_findapp("MixMonitor")) {
492 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
493 cap = ast_format_cap_destroy(cap);
494 ao2_unlock(conference_bridge);
497 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
498 if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) {
499 cap = ast_format_cap_destroy(cap);
500 ao2_unlock(conference_bridge);
504 cap = ast_format_cap_destroy(cap);
505 ao2_ref(conference_bridge, +1); /* give the record thread a ref */
507 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
508 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
510 ao2_unlock(conference_bridge);
511 ao2_ref(conference_bridge, -1); /* error so remove ref */
515 ao2_unlock(conference_bridge);
519 static void send_conf_start_event(const char *conf_name)
521 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
524 static void send_conf_end_event(const char *conf_name)
526 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
529 static void send_join_event(struct ast_channel *chan, const char *conf_name)
531 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
535 "CallerIDnum: %s\r\n"
536 "CallerIDname: %s\r\n",
540 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, "<unknown>"),
541 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>")
545 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
547 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
551 "CallerIDnum: %s\r\n"
552 "CallerIDname: %s\r\n",
556 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, "<unknown>"),
557 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>")
562 * \brief Announce number of users in the conference bridge to the caller
564 * \param conference_bridge Conference bridge to peek at
565 * \param (OPTIONAL) conference_bridge_user Caller
567 * \note if caller is NULL, the announcment will be sent to all participants in the conference.
568 * \return Returns 0 on success, -1 if the user hung up
570 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
572 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
573 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
574 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
576 if (conference_bridge->users == 1) {
577 /* Awww we are the only person in the conference bridge */
579 } else if (conference_bridge->users == 2) {
580 if (conference_bridge_user) {
581 /* Eep, there is one other person */
582 if (ast_stream_and_wait(conference_bridge_user->chan,
588 play_sound_file(conference_bridge, only_one);
591 /* Alas multiple others in here */
592 if (conference_bridge_user) {
593 if (ast_stream_and_wait(conference_bridge_user->chan,
598 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
601 if (ast_stream_and_wait(conference_bridge_user->chan,
607 play_sound_file(conference_bridge, there_are);
608 play_sound_number(conference_bridge, conference_bridge->users - 1);
609 play_sound_file(conference_bridge, other_in_party);
616 * \brief Play back an audio file to a channel
618 * \param conference_bridge Conference bridge they are in
619 * \param chan Channel to play audio prompt to
620 * \param file Prompt to play
622 * \return Returns 0 on success, -1 if the user hung up
624 * \note This function assumes that conference_bridge is locked
626 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
629 ao2_unlock(conference_bridge);
630 res = ast_stream_and_wait(chan, file, "");
631 ao2_lock(conference_bridge);
635 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
637 /* Right now, only marked users are automatically set as the single src of video.*/
642 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
644 struct conference_bridge_user *tmp_user = NULL;
645 ao2_lock(conference_bridge);
646 /* see if anyone is already the video src */
647 AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
648 if (tmp_user->chan == chan) {
651 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
656 ao2_unlock(conference_bridge);
658 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
660 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
661 /* we joined and are video capable, we override anyone else that may have already been the video feed */
662 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
666 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
668 struct conference_bridge_user *tmp_user = NULL;
670 /* if this isn't a video source, nothing to update */
671 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
675 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
677 /* If in follow talker mode, make sure to restore this mode on the
678 * bridge when a source is removed. It is possible this channel was
679 * only set temporarily as a video source by an AMI or DTMF action. */
680 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
681 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
684 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
685 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
686 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
690 /* Make the next available marked user the video src. */
691 ao2_lock(conference_bridge);
692 AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
693 if (tmp_user->chan == chan) {
696 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
697 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
701 ao2_unlock(conference_bridge);
705 * \brief Perform post-joining marked specific actions
707 * \param conference_bridge Conference bridge being joined
708 * \param conference_bridge_user Conference bridge user joining
710 * \return Returns 0 on success, -1 if the user hung up
712 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
714 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
715 struct conference_bridge_user *other_conference_bridge_user = NULL;
717 /* If we are not the first user to join, then the users are already
718 * in the conference so we do not need to update them. */
719 if (conference_bridge->markedusers >= 2) {
723 /* Iterate through every participant stopping MOH on them if need be */
724 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
725 if (other_conference_bridge_user == conference_bridge_user) {
728 if (other_conference_bridge_user->playing_moh && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
729 other_conference_bridge_user->playing_moh = 0;
730 ast_moh_stop(other_conference_bridge_user->chan);
731 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
735 /* Next play the audio file stating they are going to be placed into the conference */
736 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
737 ao2_unlock(conference_bridge);
738 ast_autoservice_start(conference_bridge_user->chan);
739 play_sound_file(conference_bridge,
740 conf_get_sound(CONF_SOUND_PLACE_IN_CONF, conference_bridge_user->b_profile.sounds));
741 ast_autoservice_stop(conference_bridge_user->chan);
742 ao2_lock(conference_bridge);
745 /* Finally iterate through and unmute them all */
746 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
747 if (other_conference_bridge_user == conference_bridge_user) {
750 /* only unmute them if they are not supposed to start muted */
751 if (!ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_STARTMUTED)) {
752 other_conference_bridge_user->features.mute = 0;
756 /* If a marked user already exists in the conference bridge we can just bail out now */
757 if (conference_bridge->markedusers) {
760 /* Be sure we are muted so we can't talk to anybody else waiting */
761 conference_bridge_user->features.mute = 1;
762 /* If we have not been quieted play back that they are waiting for the leader */
763 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
764 if (play_prompt_to_channel(conference_bridge,
765 conference_bridge_user->chan,
766 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds))) {
767 /* user hungup while the sound was playing */
771 /* Start music on hold if needed */
772 /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
773 * allowing a marked user to enter while the prompt was playing
775 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
776 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
777 conference_bridge_user->playing_moh = 1;
784 * \brief Perform post-joining non-marked specific actions
786 * \param conference_bridge Conference bridge being joined
787 * \param conference_bridge_user Conference bridge user joining
789 * \return Returns 0 on success, -1 if the user hung up
791 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
793 /* Play back audio prompt and start MOH if need be if we are the first participant */
794 if (conference_bridge->users == 1) {
795 /* If audio prompts have not been quieted or this prompt quieted play it on out */
796 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
797 if (play_prompt_to_channel(conference_bridge,
798 conference_bridge_user->chan,
799 conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds))) {
800 /* user hungup while the sound was playing */
804 /* If we need to start music on hold on the channel do so now */
805 /* We need to re-check the number of users in the conference bridge here because another conference bridge
806 * participant could have joined while the above prompt was playing for the first user.
808 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
809 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
810 conference_bridge_user->playing_moh = 1;
815 /* Announce number of users if need be */
816 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
817 ao2_unlock(conference_bridge);
818 if (announce_user_count(conference_bridge, conference_bridge_user)) {
819 ao2_lock(conference_bridge);
822 ao2_lock(conference_bridge);
825 /* If we are the second participant we may need to stop music on hold on the first */
826 if (conference_bridge->users == 2) {
827 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
829 /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
830 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
831 first_participant->playing_moh = 0;
832 ast_moh_stop(first_participant->chan);
833 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
837 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
838 (conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) {
839 ao2_unlock(conference_bridge);
840 if (announce_user_count(conference_bridge, NULL)) {
841 ao2_lock(conference_bridge);
844 ao2_lock(conference_bridge);
850 * \brief Destroy a conference bridge
852 * \param obj The conference bridge object
854 * \return Returns nothing
856 static void destroy_conference_bridge(void *obj)
858 struct conference_bridge *conference_bridge = obj;
860 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
862 ast_mutex_destroy(&conference_bridge->playback_lock);
864 if (conference_bridge->playback_chan) {
865 struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
866 ast_hangup(underlying_channel);
867 ast_hangup(conference_bridge->playback_chan);
868 conference_bridge->playback_chan = NULL;
871 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
872 if (conference_bridge->bridge) {
873 ast_bridge_destroy(conference_bridge->bridge);
874 conference_bridge->bridge = NULL;
876 conf_bridge_profile_destroy(&conference_bridge->b_profile);
879 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
882 * \brief Join a conference bridge
884 * \param name The conference name
885 * \param conference_bridge_user Conference bridge user structure
887 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
889 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
891 struct conference_bridge *conference_bridge = NULL;
892 struct conference_bridge tmp;
893 int start_record = 0;
894 int max_members_reached = 0;
896 ast_copy_string(tmp.name, name, sizeof(tmp.name));
898 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
899 ao2_lock(conference_bridges);
901 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
903 /* Attempt to find an existing conference bridge */
904 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
906 if (conference_bridge && conference_bridge->b_profile.max_members) {
907 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->users ? 0 : 1;
910 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
911 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
912 ao2_unlock(conference_bridges);
913 ao2_ref(conference_bridge, -1);
914 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
915 ast_stream_and_wait(conference_bridge_user->chan,
916 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
921 /* If no conference bridge was found see if we can create one */
922 if (!conference_bridge) {
923 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
924 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
925 ao2_unlock(conference_bridges);
926 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
930 /* Setup conference bridge parameters */
931 conference_bridge->record_thread = AST_PTHREADT_NULL;
932 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
933 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
935 /* Create an actual bridge that will do the audio mixing */
936 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
937 ao2_ref(conference_bridge, -1);
938 conference_bridge = NULL;
939 ao2_unlock(conference_bridges);
940 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
944 /* Set the internal sample rate on the bridge from the bridge profile */
945 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
946 /* Set the internal mixing interval on the bridge from the bridge profile */
947 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
949 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
950 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
953 /* Setup lock for playback channel */
954 ast_mutex_init(&conference_bridge->playback_lock);
956 /* Link it into the conference bridges container */
957 ao2_link(conference_bridges, conference_bridge);
960 send_conf_start_event(conference_bridge->name);
961 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
964 ao2_unlock(conference_bridges);
966 /* Setup conference bridge user parameters */
967 conference_bridge_user->conference_bridge = conference_bridge;
969 ao2_lock(conference_bridge);
971 /* All good to go, add them in */
972 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
974 /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
975 conference_bridge->users++;
977 /* If the caller is a marked user bump up the count */
978 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
979 conference_bridge->markedusers++;
982 /* Set the device state for this conference */
983 if (conference_bridge->users == 1) {
984 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
987 /* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
988 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER | USER_OPT_WAITMARKED)) {
989 if (post_join_marked(conference_bridge, conference_bridge_user)) {
990 ao2_unlock(conference_bridge);
991 leave_conference_bridge(conference_bridge, conference_bridge_user);
995 if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
996 ao2_unlock(conference_bridge);
997 leave_conference_bridge(conference_bridge, conference_bridge_user);
1002 /* check to see if recording needs to be started or not */
1003 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE) && !conf_is_recording(conference_bridge)) {
1007 ao2_unlock(conference_bridge);
1010 conf_start_record(conference_bridge);
1013 return conference_bridge;
1017 * \brief Leave a conference bridge
1019 * \param conference_bridge The conference bridge to leave
1020 * \param conference_bridge_user The conference bridge user structure
1023 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1025 ao2_lock(conference_bridge);
1027 /* If this caller is a marked user bump down the count */
1028 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
1029 conference_bridge->markedusers--;
1032 /* Decrement the users count while keeping the previous participant count */
1033 conference_bridge->users--;
1035 /* Drop conference bridge user from the list, they be going bye bye */
1036 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
1038 /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
1039 if (conference_bridge->users) {
1040 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER) && !conference_bridge->markedusers) {
1041 struct conference_bridge_user *other_participant = NULL;
1043 /* Start out with muting everyone */
1044 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
1045 other_participant->features.mute = 1;
1048 /* Play back the audio prompt saying the leader has left the conference */
1049 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
1050 ao2_unlock(conference_bridge);
1051 ast_autoservice_start(conference_bridge_user->chan);
1052 play_sound_file(conference_bridge,
1053 conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, conference_bridge_user->b_profile.sounds));
1054 ast_autoservice_stop(conference_bridge_user->chan);
1055 ao2_lock(conference_bridge);
1058 /* Now on to starting MOH or kick if needed */
1059 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
1060 if (ast_test_flag(&other_participant->u_profile, USER_OPT_ENDMARKED)) {
1061 other_participant->kicked = 1;
1062 ast_bridge_remove(conference_bridge->bridge, other_participant->chan);
1063 } else if (ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
1064 ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL);
1065 other_participant->playing_moh = 1;
1066 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
1069 } else if (conference_bridge->users == 1) {
1070 /* Of course if there is one other person in here we may need to start up MOH on them */
1071 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
1073 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
1074 ast_moh_start(first_participant->chan, first_participant->u_profile.moh_class, NULL);
1075 first_participant->playing_moh = 1;
1076 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
1080 /* Set device state to "not in use" */
1081 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
1083 ao2_unlink(conference_bridges, conference_bridge);
1084 send_conf_end_event(conference_bridge->name);
1087 /* Done mucking with the conference bridge, huzzah */
1088 ao2_unlock(conference_bridge);
1090 if (!conference_bridge->users) {
1091 conf_stop_record(conference_bridge);
1094 ao2_ref(conference_bridge, -1);
1099 * \brief allocates playback chan on a channel
1100 * \pre expects conference to be locked before calling this function
1102 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
1105 struct ast_format_cap *cap;
1106 struct ast_format tmpfmt;
1108 if (conference_bridge->playback_chan) {
1111 if (!(cap = ast_format_cap_alloc_nolock())) {
1114 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
1115 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
1116 cap = ast_format_cap_destroy(cap);
1119 cap = ast_format_cap_destroy(cap);
1121 conference_bridge->playback_chan->bridge = conference_bridge->bridge;
1123 if (ast_call(conference_bridge->playback_chan, "", 0)) {
1124 ast_hangup(conference_bridge->playback_chan);
1125 conference_bridge->playback_chan = NULL;
1129 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
1133 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
1135 struct ast_channel *underlying_channel;
1137 ast_mutex_lock(&conference_bridge->playback_lock);
1138 if (!(conference_bridge->playback_chan)) {
1139 if (alloc_playback_chan(conference_bridge)) {
1140 ast_mutex_unlock(&conference_bridge->playback_lock);
1143 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
1145 /* Channel was already available so we just need to add it back into the bridge */
1146 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
1147 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
1150 /* The channel is all under our control, in goes the prompt */
1151 if (!ast_strlen_zero(filename)) {
1152 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
1154 ast_say_number(conference_bridge->playback_chan, say_number, "", conference_bridge->playback_chan->language, NULL);
1157 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
1158 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
1160 ast_mutex_unlock(&conference_bridge->playback_lock);
1166 * \brief Play sound file into conference bridge
1168 * \param conference_bridge The conference bridge to play sound file into
1169 * \param filename Sound file to play
1172 * \retval -1 failure
1174 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
1176 return play_sound_helper(conference_bridge, filename, 0);
1180 * \brief Play number into the conference bridge
1182 * \param conference_bridge The conference bridge to say the number into
1183 * \param number to say
1186 * \retval -1 failure
1188 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
1190 return play_sound_helper(conference_bridge, NULL, say_number);
1193 static void conf_handle_talker_destructor(void *pvt_data)
1198 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
1200 char *conf_name = pvt_data;
1203 switch (bridge_channel->state) {
1204 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
1207 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
1211 return; /* uhh this shouldn't happen, but bail if it does. */
1214 /* notify AMI someone is has either started or stopped talking */
1215 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
1218 "Conference: %s\r\n"
1219 "TalkingStatus: %s\r\n",
1220 bridge_channel->chan->name, bridge_channel->chan->uniqueid, conf_name, talking ? "on" : "off");
1223 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
1225 char pin_guess[MAX_PIN+1] = { 0, };
1226 const char *pin = conference_bridge_user->u_profile.pin;
1227 char *tmp = pin_guess;
1229 unsigned int len = MAX_PIN ;
1231 /* give them three tries to get the pin right */
1232 for (i = 0; i < 3; i++) {
1233 if (ast_app_getdata(chan,
1234 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
1235 tmp, len, 0) >= 0) {
1236 if (!strcasecmp(pin, pin_guess)) {
1240 ast_streamfile(chan,
1241 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
1243 res = ast_waitstream(chan, AST_DIGIT_ANY);
1245 /* Account for digit already read during ivalid pin playback
1246 * resetting pin buf. */
1248 pin_guess[1] = '\0';
1249 tmp = pin_guess + 1;
1252 /* reset pin buf as empty buffer. */
1260 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
1262 char destdir[PATH_MAX];
1266 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1268 if (ast_mkdir(destdir, 0777) != 0) {
1269 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1272 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1273 "%s/confbridge-name-%s-%s", destdir,
1274 conf_name, user->chan->uniqueid);
1276 res = ast_play_and_record(user->chan,
1278 user->name_rec_location,
1282 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1287 user->name_rec_location[0] = '\0';
1293 /*! \brief The ConfBridge application */
1294 static int confbridge_exec(struct ast_channel *chan, const char *data)
1296 int res = 0, volume_adjustments[2];
1299 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
1300 const char *u_profile_name = DEFAULT_USER_PROFILE;
1301 struct conference_bridge *conference_bridge = NULL;
1302 struct conference_bridge_user conference_bridge_user = {
1304 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1305 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1306 .tech_args.drop_silence = 0,
1308 AST_DECLARE_APP_ARGS(args,
1309 AST_APP_ARG(conf_name);
1310 AST_APP_ARG(b_profile_name);
1311 AST_APP_ARG(u_profile_name);
1312 AST_APP_ARG(menu_name);
1314 ast_bridge_features_init(&conference_bridge_user.features);
1316 if (chan->_state != AST_STATE_UP) {
1320 if (ast_strlen_zero(data)) {
1321 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1322 res = -1; /* invalid PIN */
1323 goto confbridge_cleanup;
1326 /* We need to make a copy of the input string if we are going to modify it! */
1327 parse = ast_strdupa(data);
1329 AST_STANDARD_APP_ARGS(args, parse);
1331 /* bridge profile name */
1332 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1333 b_profile_name = args.b_profile_name;
1335 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
1336 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
1338 goto confbridge_cleanup;
1341 /* user profile name */
1342 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1343 u_profile_name = args.u_profile_name;
1346 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
1347 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
1349 goto confbridge_cleanup;
1351 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
1353 /* ask for a PIN immediately after finding user profile. This has to be
1354 * prompted for requardless of quiet setting. */
1355 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
1356 if (conf_get_pin(chan, &conference_bridge_user)) {
1357 res = -1; /* invalid PIN */
1358 goto confbridge_cleanup;
1362 /* See if we need them to record a intro name */
1363 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
1364 conf_rec_name(&conference_bridge_user, args.conf_name);
1368 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
1369 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
1370 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
1371 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
1373 res = -1; /* invalid PIN */
1374 goto confbridge_cleanup;
1378 /* Set if DTMF should pass through for this user or not */
1379 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
1380 conference_bridge_user.features.dtmf_passthrough = 1;
1383 /* Set dsp threshold values if present */
1384 if (conference_bridge_user.u_profile.talking_threshold) {
1385 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
1387 if (conference_bridge_user.u_profile.silence_threshold) {
1388 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
1391 /* Set a talker indicate call back if talking detection is requested */
1392 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
1393 char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
1395 res = -1; /* invalid PIN */
1396 goto confbridge_cleanup;
1398 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
1399 conf_handle_talker_cb,
1400 conf_handle_talker_destructor,
1404 /* Look for a conference bridge matching the provided name */
1405 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
1406 res = -1; /* invalid PIN */
1407 goto confbridge_cleanup;
1410 /* Keep a copy of volume adjustments so we can restore them later if need be */
1411 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1412 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1414 /* If the caller should be joined already muted, make it so */
1415 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
1416 conference_bridge_user.features.mute = 1;
1419 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
1420 conference_bridge_user.tech_args.drop_silence = 1;
1423 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
1425 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1427 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1431 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
1433 /* Reduce background noise from each participant */
1434 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1435 ast_free(mod_speex);
1436 ast_func_write(chan, "DENOISE(rx)", "on");
1440 /* if this user has a intro, play it before entering */
1441 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1442 ast_autoservice_start(chan);
1443 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1444 play_sound_file(conference_bridge,
1445 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
1446 ast_autoservice_stop(chan);
1449 /* Play the Join sound to both the conference and the user entering. */
1451 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
1452 if (conference_bridge_user.playing_moh) {
1455 ast_stream_and_wait(chan, join_sound, "");
1456 ast_autoservice_start(chan);
1457 play_sound_file(conference_bridge, join_sound);
1458 ast_autoservice_stop(chan);
1459 if (conference_bridge_user.playing_moh) {
1460 ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
1464 /* See if we need to automatically set this user as a video source or not */
1465 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
1467 /* Join our conference bridge for real */
1468 send_join_event(conference_bridge_user.chan, conference_bridge->name);
1469 ast_bridge_join(conference_bridge->bridge,
1472 &conference_bridge_user.features,
1473 &conference_bridge_user.tech_args);
1474 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
1476 /* If this user was a video source, we need to clean up and possibly pick a new source. */
1477 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
1479 /* if this user has a intro, play it when leaving */
1480 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1481 ast_autoservice_start(chan);
1482 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1483 play_sound_file(conference_bridge,
1484 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
1485 ast_autoservice_stop(chan);
1488 /* play the leave sound */
1490 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
1491 ast_autoservice_start(chan);
1492 play_sound_file(conference_bridge, leave_sound);
1493 ast_autoservice_stop(chan);
1496 /* Easy as pie, depart this channel from the conference bridge */
1497 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1498 conference_bridge = NULL;
1500 /* If the user was kicked from the conference play back the audio prompt for it */
1501 if (!quiet && conference_bridge_user.kicked) {
1502 res = ast_stream_and_wait(chan,
1503 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
1507 /* Restore volume adjustments to previous values in case they were changed */
1508 if (volume_adjustments[0]) {
1509 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1511 if (volume_adjustments[1]) {
1512 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1515 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1516 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
1520 ast_bridge_features_cleanup(&conference_bridge_user.features);
1521 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
1525 static int action_toggle_mute(struct conference_bridge *conference_bridge,
1526 struct conference_bridge_user *conference_bridge_user,
1527 struct ast_channel *chan)
1529 /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
1530 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
1531 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
1533 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
1534 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
1535 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
1539 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1541 char *file_copy = ast_strdupa(playback_file);
1544 while ((file = strsep(&file_copy, "&"))) {
1545 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1546 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1553 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
1554 struct conference_bridge_user *conference_bridge_user,
1555 struct ast_bridge_channel *bridge_channel,
1556 struct conf_menu *menu,
1557 const char *playback_file,
1558 const char *cur_dtmf,
1563 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1564 struct conf_menu_entry new_menu_entry = { { 0, }, };
1565 char *file_copy = ast_strdupa(playback_file);
1568 while ((file = strsep(&file_copy, "&"))) {
1569 if (ast_streamfile(bridge_channel->chan, file, bridge_channel->chan->language)) {
1570 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1574 /* now wait for more digits. */
1575 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1576 /* streaming finished and no DTMF was entered */
1578 } else if (digit == -1) {
1582 break; /* dtmf was entered */
1586 /* streaming finished on all files and no DTMF was entered */
1589 ast_stopstream(bridge_channel->chan);
1591 /* If we get here, then DTMF has been entered, This means no
1592 * additional prompts should be played for this menu entry */
1595 /* If a digit was pressed during the payback, update
1596 * the dtmf string and look for a new menu entry in the
1598 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1599 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1600 dtmf[i] = cur_dtmf[i];
1602 dtmf[i] = (char) digit;
1608 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1609 * If this is the case, no new DTMF sequence should be looked for. */
1614 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1615 execute_menu_entry(conference_bridge,
1616 conference_bridge_user,
1618 &new_menu_entry, menu);
1619 conf_menu_entry_destroy(&new_menu_entry);
1624 static int action_kick_last(struct conference_bridge *conference_bridge,
1625 struct ast_bridge_channel *bridge_channel,
1626 struct conference_bridge_user *conference_bridge_user)
1628 struct conference_bridge_user *last_participant = NULL;
1629 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1632 ast_stream_and_wait(bridge_channel->chan,
1633 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1635 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1636 bridge_channel->chan->name,
1637 conference_bridge->name);
1641 ao2_lock(conference_bridge);
1642 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user)
1643 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
1644 ao2_unlock(conference_bridge);
1645 ast_stream_and_wait(bridge_channel->chan,
1646 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1648 } else if (last_participant) {
1649 last_participant->kicked = 1;
1650 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
1651 ao2_unlock(conference_bridge);
1656 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1658 struct ast_pbx_args args;
1659 struct ast_pbx *pbx;
1665 memset(&args, 0, sizeof(args));
1666 args.no_hangup_chan = 1;
1668 ast_channel_lock(bridge_channel->chan);
1671 exten = ast_strdupa(bridge_channel->chan->exten);
1672 context = ast_strdupa(bridge_channel->chan->context);
1673 priority = bridge_channel->chan->priority;
1674 pbx = bridge_channel->chan->pbx;
1675 bridge_channel->chan->pbx = NULL;
1678 ast_copy_string(bridge_channel->chan->exten, menu_action->data.dialplan_args.exten, sizeof(bridge_channel->chan->exten));
1679 ast_copy_string(bridge_channel->chan->context, menu_action->data.dialplan_args.context, sizeof(bridge_channel->chan->context));
1680 bridge_channel->chan->priority = menu_action->data.dialplan_args.priority;
1682 ast_channel_unlock(bridge_channel->chan);
1685 res = ast_pbx_run_args(bridge_channel->chan, &args);
1688 ast_channel_lock(bridge_channel->chan);
1690 ast_copy_string(bridge_channel->chan->exten, exten, sizeof(bridge_channel->chan->exten));
1691 ast_copy_string(bridge_channel->chan->context, context, sizeof(bridge_channel->chan->context));
1692 bridge_channel->chan->priority = priority;
1693 bridge_channel->chan->pbx = pbx;
1695 ast_channel_unlock(bridge_channel->chan);
1700 static int execute_menu_entry(struct conference_bridge *conference_bridge,
1701 struct conference_bridge_user *conference_bridge_user,
1702 struct ast_bridge_channel *bridge_channel,
1703 struct conf_menu_entry *menu_entry,
1704 struct conf_menu *menu)
1706 struct conf_menu_action *menu_action;
1707 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1708 int stop_prompts = 0;
1711 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1712 switch (menu_action->id) {
1713 case MENU_ACTION_TOGGLE_MUTE:
1714 res |= action_toggle_mute(conference_bridge,
1715 conference_bridge_user,
1716 bridge_channel->chan);
1718 case MENU_ACTION_PLAYBACK:
1719 if (!stop_prompts) {
1720 res |= action_playback(bridge_channel, menu_action->data.playback_file);
1723 case MENU_ACTION_RESET_LISTENING:
1724 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
1726 case MENU_ACTION_RESET_TALKING:
1727 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
1729 case MENU_ACTION_INCREASE_LISTENING:
1730 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1731 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
1733 case MENU_ACTION_DECREASE_LISTENING:
1734 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1735 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
1737 case MENU_ACTION_INCREASE_TALKING:
1738 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1739 AST_AUDIOHOOK_DIRECTION_READ, 1);
1741 case MENU_ACTION_DECREASE_TALKING:
1742 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1743 AST_AUDIOHOOK_DIRECTION_READ, -1);
1745 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1746 if (!(stop_prompts)) {
1747 res |= action_playback_and_continue(conference_bridge,
1748 conference_bridge_user,
1751 menu_action->data.playback_file,
1756 case MENU_ACTION_DIALPLAN_EXEC:
1757 res |= action_dialplan_exec(bridge_channel, menu_action);
1759 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1763 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
1764 res |= ast_stream_and_wait(bridge_channel->chan,
1765 (conference_bridge->locked ?
1766 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
1767 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
1771 case MENU_ACTION_ADMIN_KICK_LAST:
1772 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
1774 case MENU_ACTION_LEAVE:
1775 ao2_lock(conference_bridge);
1776 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
1777 ao2_unlock(conference_bridge);
1779 case MENU_ACTION_NOOP:
1781 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1782 ao2_lock(conference_bridge);
1783 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
1784 ao2_unlock(conference_bridge);
1786 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1787 handle_video_on_exit(conference_bridge, bridge_channel->chan);
1794 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
1795 struct conference_bridge_user *conference_bridge_user,
1796 struct conf_menu_entry *menu_entry,
1797 struct conf_menu *menu)
1799 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
1801 /* See if music on hold is playing */
1802 ao2_lock(conference_bridge);
1803 if (conference_bridge_user->playing_moh) {
1804 /* MOH is going, let's stop it */
1805 ast_moh_stop(bridge_channel->chan);
1807 ao2_unlock(conference_bridge);
1809 /* execute the list of actions associated with this menu entry */
1810 execute_menu_entry(conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
1812 /* See if music on hold needs to be started back up again */
1813 ao2_lock(conference_bridge);
1814 if (conference_bridge_user->playing_moh) {
1815 ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL);
1817 ao2_unlock(conference_bridge);
1822 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
1825 struct conference_bridge *bridge = NULL;
1827 int wordlen = strlen(word);
1828 struct ao2_iterator i;
1830 i = ao2_iterator_init(conference_bridges, 0);
1831 while ((bridge = ao2_iterator_next(&i))) {
1832 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
1833 res = ast_strdup(bridge->name);
1834 ao2_ref(bridge, -1);
1837 ao2_ref(bridge, -1);
1839 ao2_iterator_destroy(&i);
1844 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1846 struct conference_bridge *bridge = NULL;
1847 struct conference_bridge tmp;
1848 struct conference_bridge_user *participant = NULL;
1852 e->command = "confbridge kick";
1854 "Usage: confbridge kick <conference> <channel>\n"
1855 " Kicks a channel out of the conference bridge.\n";
1859 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
1863 return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
1870 return CLI_SHOWUSAGE;
1873 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
1874 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1876 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
1880 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
1881 if (!strncmp(a->argv[3], participant->chan->name, strlen(participant->chan->name))) {
1886 ast_cli(a->fd, "Kicking %s from confbridge %s\n", participant->chan->name, bridge->name);
1887 participant->kicked = 1;
1888 ast_bridge_remove(bridge->bridge, participant->chan);
1891 ao2_ref(bridge, -1);
1895 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1897 struct ao2_iterator i;
1898 struct conference_bridge *bridge = NULL;
1899 struct conference_bridge tmp;
1900 struct conference_bridge_user *participant = NULL;
1904 e->command = "confbridge list";
1906 "Usage: confbridge list [<name>]\n"
1907 " Lists all currently active conference bridges.\n";
1911 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
1917 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
1918 ast_cli(a->fd, "================================ ====== ====== ========\n");
1919 i = ao2_iterator_init(conference_bridges, 0);
1920 while ((bridge = ao2_iterator_next(&i))) {
1921 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->users, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
1922 ao2_ref(bridge, -1);
1924 ao2_iterator_destroy(&i);
1929 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
1930 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1932 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
1935 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu\n");
1936 ast_cli(a->fd, "============================= ================ ================ ================\n");
1938 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
1939 ast_cli(a->fd, "%-29s ", participant->chan->name);
1940 ast_cli(a->fd, "%-17s", participant->u_profile.name);
1941 ast_cli(a->fd, "%-17s", participant->b_profile.name);
1942 ast_cli(a->fd, "%-17s", participant->menu_name);
1943 ast_cli(a->fd, "\n");
1946 ao2_ref(bridge, -1);
1950 return CLI_SHOWUSAGE;
1954 * \brief finds a conference by name and locks/unlocks.
1957 * \retval -1 conference not found
1959 static int generic_lock_unlock_helper(int lock, const char *conference)
1961 struct conference_bridge *bridge = NULL;
1962 struct conference_bridge tmp;
1965 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
1966 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1971 bridge->locked = lock;
1973 ao2_ref(bridge, -1);
1979 * \brief finds a conference user by channel name and mutes/unmutes them.
1982 * \retval -1 conference not found
1983 * \retval -2 user not found
1985 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
1987 struct conference_bridge *bridge = NULL;
1988 struct conference_bridge tmp;
1989 struct conference_bridge_user *participant = NULL;
1991 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
1992 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1997 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
1998 if (!strncmp(user, participant->chan->name, strlen(user))) {
2003 participant->features.mute = mute;
2008 ao2_ref(bridge, -1);
2013 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2015 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2018 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2020 } else if (res == -2) {
2021 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2024 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2028 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2032 e->command = "confbridge mute";
2034 "Usage: confbridge mute <conference> <channel>\n";
2038 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2043 return CLI_SHOWUSAGE;
2046 cli_mute_unmute_helper(1, a);
2051 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2055 e->command = "confbridge unmute";
2057 "Usage: confbridge unmute <conference> <channel>\n";
2061 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2066 return CLI_SHOWUSAGE;
2069 cli_mute_unmute_helper(0, a);
2074 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2078 e->command = "confbridge lock";
2080 "Usage: confbridge lock <conference>\n";
2084 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2089 return CLI_SHOWUSAGE;
2091 if (generic_lock_unlock_helper(1, a->argv[2])) {
2092 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2094 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2099 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2103 e->command = "confbridge unlock";
2105 "Usage: confbridge unlock <conference>\n";
2109 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2114 return CLI_SHOWUSAGE;
2116 if (generic_lock_unlock_helper(0, a->argv[2])) {
2117 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2119 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2124 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2126 const char *rec_file = NULL;
2127 struct conference_bridge *bridge = NULL;
2128 struct conference_bridge tmp;
2132 e->command = "confbridge record start";
2134 "Usage: confbridge record start <conference> <file>\n"
2135 " <file> is optional, Otherwise the bridge profile\n"
2136 " record file will be used. If the bridge profile\n"
2137 " has no record file specified, a file will automatically\n"
2138 " be generated in the monitor directory\n";
2142 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2147 return CLI_SHOWUSAGE;
2150 rec_file = a->argv[4];
2153 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2154 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2156 ast_cli(a->fd, "Conference not found.\n");
2159 if (conf_is_recording(bridge)) {
2160 ast_cli(a->fd, "Conference is already being recorded.\n");
2161 ao2_ref(bridge, -1);
2164 if (!ast_strlen_zero(rec_file)) {
2166 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2169 if (conf_start_record(bridge)) {
2170 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2171 ao2_ref(bridge, -1);
2174 ast_cli(a->fd, "Recording started\n");
2175 ao2_ref(bridge, -1);
2179 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2181 struct conference_bridge *bridge = NULL;
2182 struct conference_bridge tmp;
2186 e->command = "confbridge record stop";
2188 "Usage: confbridge record stop <conference>\n";
2192 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2197 return CLI_SHOWUSAGE;
2200 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2201 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2203 ast_cli(a->fd, "Conference not found.\n");
2206 conf_stop_record(bridge);
2207 ast_cli(a->fd, "Recording stopped.\n");
2208 ao2_ref(bridge, -1);
2212 static struct ast_cli_entry cli_confbridge[] = {
2213 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2214 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2215 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2216 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Mute a participant."),
2217 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2218 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2219 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2220 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2222 static struct ast_custom_function confbridge_function = {
2223 .name = "CONFBRIDGE",
2224 .write = func_confbridge_helper,
2227 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2228 static struct ast_custom_function confbridge_info_function = {
2229 .name = "CONFBRIDGE_INFO",
2230 .read = func_confbridge_info,
2233 static int action_confbridgelist(struct mansession *s, const struct message *m)
2235 const char *actionid = astman_get_header(m, "ActionID");
2236 const char *conference = astman_get_header(m, "Conference");
2237 struct conference_bridge_user *participant = NULL;
2238 struct conference_bridge *bridge = NULL;
2239 struct conference_bridge tmp;
2240 char id_text[80] = "";
2243 if (!ast_strlen_zero(actionid)) {
2244 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2246 if (ast_strlen_zero(conference)) {
2247 astman_send_error(s, m, "No Conference name provided.");
2250 if (!ao2_container_count(conference_bridges)) {
2251 astman_send_error(s, m, "No active conferences.");
2254 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2255 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2257 astman_send_error(s, m, "No Conference by that name found.");
2261 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2264 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2267 "Event: ConfbridgeList\r\n"
2269 "Conference: %s\r\n"
2270 "CallerIDNum: %s\r\n"
2271 "CallerIDName: %s\r\n"
2274 "MarkedUser: %s\r\n"
2278 S_COR(participant->chan->caller.id.number.valid, participant->chan->caller.id.number.str, "<unknown>"),
2279 S_COR(participant->chan->caller.id.name.valid, participant->chan->caller.id.name.str, "<no name>"),
2280 participant->chan->name,
2281 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2282 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
2285 ao2_ref(bridge, -1);
2288 "Event: ConfbridgeListComplete\r\n"
2289 "EventList: Complete\r\n"
2292 "\r\n", total, id_text);
2297 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2299 const char *actionid = astman_get_header(m, "ActionID");
2300 struct conference_bridge *bridge = NULL;
2301 struct ao2_iterator i;
2302 char id_text[512] = "";
2305 if (!ast_strlen_zero(actionid)) {
2306 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2309 if (!ao2_container_count(conference_bridges)) {
2310 astman_send_error(s, m, "No active conferences.");
2314 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2316 /* Traverse the conference list */
2317 i = ao2_iterator_init(conference_bridges, 0);
2318 while ((bridge = ao2_iterator_next(&i))) {
2323 "Event: ConfbridgeListRooms\r\n"
2325 "Conference: %s\r\n"
2333 bridge->markedusers,
2334 bridge->locked ? "Yes" : "No");
2337 ao2_ref(bridge, -1);
2339 ao2_iterator_destroy(&i);
2341 /* Send final confirmation */
2343 "Event: ConfbridgeListRoomsComplete\r\n"
2344 "EventList: Complete\r\n"
2347 "\r\n", totalitems, id_text);
2351 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2353 const char *conference = astman_get_header(m, "Conference");
2354 const char *channel = astman_get_header(m, "Channel");
2357 if (ast_strlen_zero(conference)) {
2358 astman_send_error(s, m, "No Conference name provided.");
2361 if (ast_strlen_zero(channel)) {
2362 astman_send_error(s, m, "No channel name provided.");
2365 if (!ao2_container_count(conference_bridges)) {
2366 astman_send_error(s, m, "No active conferences.");
2370 res = generic_mute_unmute_helper(mute, conference, channel);
2373 astman_send_error(s, m, "No Conference by that name found.");
2375 } else if (res == -2) {
2376 astman_send_error(s, m, "No Channel by that name found in Conference.");
2380 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2384 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2386 return action_mute_unmute_helper(s, m, 0);
2388 static int action_confbridgemute(struct mansession *s, const struct message *m)
2390 return action_mute_unmute_helper(s, m, 1);
2393 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2395 const char *conference = astman_get_header(m, "Conference");
2398 if (ast_strlen_zero(conference)) {
2399 astman_send_error(s, m, "No Conference name provided.");
2402 if (!ao2_container_count(conference_bridges)) {
2403 astman_send_error(s, m, "No active conferences.");
2406 if ((res = generic_lock_unlock_helper(lock, conference))) {
2407 astman_send_error(s, m, "No Conference by that name found.");
2410 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2413 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2415 return action_lock_unlock_helper(s, m, 0);
2417 static int action_confbridgelock(struct mansession *s, const struct message *m)
2419 return action_lock_unlock_helper(s, m, 1);
2422 static int action_confbridgekick(struct mansession *s, const struct message *m)
2424 const char *conference = astman_get_header(m, "Conference");
2425 const char *channel = astman_get_header(m, "Channel");
2426 struct conference_bridge_user *participant = NULL;
2427 struct conference_bridge *bridge = NULL;
2428 struct conference_bridge tmp;
2431 if (ast_strlen_zero(conference)) {
2432 astman_send_error(s, m, "No Conference name provided.");
2435 if (!ao2_container_count(conference_bridges)) {
2436 astman_send_error(s, m, "No active conferences.");
2439 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2440 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2442 astman_send_error(s, m, "No Conference by that name found.");
2447 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2448 if (!strcasecmp(participant->chan->name, channel)) {
2449 participant->kicked = 1;
2450 ast_bridge_remove(bridge->bridge, participant->chan);
2456 ao2_ref(bridge, -1);
2459 astman_send_ack(s, m, "User kicked");
2461 astman_send_error(s, m, "No Channel by that name found in Conference.");
2466 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2468 const char *conference = astman_get_header(m, "Conference");
2469 const char *recordfile = astman_get_header(m, "RecordFile");
2470 struct conference_bridge *bridge = NULL;
2471 struct conference_bridge tmp;
2473 if (ast_strlen_zero(conference)) {
2474 astman_send_error(s, m, "No Conference name provided.");
2477 if (!ao2_container_count(conference_bridges)) {
2478 astman_send_error(s, m, "No active conferences.");
2482 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2483 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2485 astman_send_error(s, m, "No Conference by that name found.");
2489 if (conf_is_recording(bridge)) {
2490 astman_send_error(s, m, "Conference is already being recorded.");
2491 ao2_ref(bridge, -1);
2495 if (!ast_strlen_zero(recordfile)) {
2497 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2501 if (conf_start_record(bridge)) {
2502 astman_send_error(s, m, "Internal error starting conference recording.");
2503 ao2_ref(bridge, -1);
2507 ao2_ref(bridge, -1);
2508 astman_send_ack(s, m, "Conference Recording Started.");
2511 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2513 const char *conference = astman_get_header(m, "Conference");
2514 struct conference_bridge *bridge = NULL;
2515 struct conference_bridge tmp;
2517 if (ast_strlen_zero(conference)) {
2518 astman_send_error(s, m, "No Conference name provided.");
2521 if (!ao2_container_count(conference_bridges)) {
2522 astman_send_error(s, m, "No active conferences.");
2526 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2527 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2529 astman_send_error(s, m, "No Conference by that name found.");
2533 if (conf_stop_record(bridge)) {
2534 astman_send_error(s, m, "Internal error while stopping recording.");
2535 ao2_ref(bridge, -1);
2539 ao2_ref(bridge, -1);
2540 astman_send_ack(s, m, "Conference Recording Stopped.");
2544 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2546 const char *conference = astman_get_header(m, "Conference");
2547 const char *channel = astman_get_header(m, "Channel");
2548 struct conference_bridge_user *participant = NULL;
2549 struct conference_bridge *bridge = NULL;
2550 struct conference_bridge tmp;
2552 if (ast_strlen_zero(conference)) {
2553 astman_send_error(s, m, "No Conference name provided.");
2556 if (ast_strlen_zero(channel)) {
2557 astman_send_error(s, m, "No channel name provided.");
2560 if (!ao2_container_count(conference_bridges)) {
2561 astman_send_error(s, m, "No active conferences.");
2565 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2566 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2568 astman_send_error(s, m, "No Conference by that name found.");
2572 /* find channel and set as video src. */
2574 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2575 if (!strncmp(channel, participant->chan->name, strlen(channel))) {
2576 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2581 ao2_ref(bridge, -1);
2583 /* do not access participant after bridge unlock. We are just
2584 * using this check to see if it was found or not */
2586 astman_send_error(s, m, "No channel by that name found in conference.");
2589 astman_send_ack(s, m, "Conference single video source set.");
2593 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2596 struct conference_bridge *bridge = NULL;
2597 struct conference_bridge_user *participant = NULL;
2598 struct conference_bridge tmp;
2600 AST_DECLARE_APP_ARGS(args,
2602 AST_APP_ARG(confno);
2605 /* parse all the required arguments and make sure they exist. */
2606 if (ast_strlen_zero(data)) {
2609 parse = ast_strdupa(data);
2610 AST_STANDARD_APP_ARGS(args, parse);
2611 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
2614 if (!ao2_container_count(conference_bridges)) {
2615 ast_log(LOG_ERROR, "No active conferneces.\n");
2618 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
2619 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2621 ast_log(LOG_ERROR, "Confernece '%s' not found.\n", args.confno);
2625 /* get the correct count for the type requested */
2627 if (!strncasecmp(args.type, "parties", 7)) {
2628 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2631 } else if (!strncasecmp(args.type, "admins", 6)) {
2632 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2633 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
2637 } else if (!strncasecmp(args.type, "marked", 6)) {
2638 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2639 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
2643 } else if (!strncasecmp(args.type, "locked", 6)) {
2644 count = bridge->locked;
2647 ao2_ref(bridge, -1);
2650 snprintf(buf, len, "%d", count);
2652 ao2_ref(bridge, -1);
2656 /*! \brief Called when module is being unloaded */
2657 static int unload_module(void)
2659 int res = ast_unregister_application(app);
2661 ast_custom_function_unregister(&confbridge_function);
2662 ast_custom_function_unregister(&confbridge_info_function);
2664 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2666 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
2667 ao2_ref(conference_bridges, -1);
2669 conf_destroy_config();
2671 ast_channel_unregister(&record_tech);
2672 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
2674 res |= ast_manager_unregister("ConfbridgeList");
2675 res |= ast_manager_unregister("ConfbridgeListRooms");
2676 res |= ast_manager_unregister("ConfbridgeMute");
2677 res |= ast_manager_unregister("ConfbridgeUnmute");
2678 res |= ast_manager_unregister("ConfbridgeKick");
2679 res |= ast_manager_unregister("ConfbridgeUnlock");
2680 res |= ast_manager_unregister("ConfbridgeLock");
2681 res |= ast_manager_unregister("ConfbridgeStartRecord");
2682 res |= ast_manager_unregister("ConfbridgeStopRecord");
2687 /*! \brief Called when module is being loaded */
2688 static int load_module(void)
2691 if ((ast_custom_function_register(&confbridge_function))) {
2692 return AST_MODULE_LOAD_FAILURE;
2694 if ((ast_custom_function_register(&confbridge_info_function))) {
2695 return AST_MODULE_LOAD_FAILURE;
2697 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
2698 return AST_MODULE_LOAD_FAILURE;
2700 ast_format_cap_add_all(record_tech.capabilities);
2701 if (ast_channel_register(&record_tech)) {
2702 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
2703 return AST_MODULE_LOAD_FAILURE;
2705 /* Create a container to hold the conference bridges */
2706 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
2707 return AST_MODULE_LOAD_FAILURE;
2709 if (ast_register_application_xml(app, confbridge_exec)) {
2710 ao2_ref(conference_bridges, -1);
2711 return AST_MODULE_LOAD_FAILURE;
2714 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2715 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
2716 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
2717 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
2718 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
2719 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
2720 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
2721 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
2722 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
2723 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
2724 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
2726 conf_load_config(0);
2730 static int reload(void)
2732 return conf_load_config(1);
2735 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
2736 .load = load_module,
2737 .unload = unload_module,
2739 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,