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 void send_conf_start_event(const char *conf_name)
385 <managerEventInstance>
386 <synopsis>Raised when a conference starts.</synopsis>
388 <parameter name="Conference">
389 <para>The name of the Confbridge conference.</para>
393 <ref type="managerEvent">ConfbridgeEnd</ref>
394 <ref type="application">ConfBridge</ref>
396 </managerEventInstance>
398 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
401 static void send_conf_end_event(const char *conf_name)
404 <managerEventInstance>
405 <synopsis>Raised when a conference ends.</synopsis>
407 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
410 <ref type="managerEvent">ConfbridgeStart</ref>
412 </managerEventInstance>
414 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
417 static void send_join_event(struct ast_channel *chan, const char *conf_name)
420 <managerEventInstance>
421 <synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
423 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
426 <ref type="managerEvent">ConfbridgeLeave</ref>
427 <ref type="application">ConfBridge</ref>
429 </managerEventInstance>
431 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
435 "CallerIDnum: %s\r\n"
436 "CallerIDname: %s\r\n",
437 ast_channel_name(chan),
438 ast_channel_uniqueid(chan),
440 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
441 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
445 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
448 <managerEventInstance>
449 <synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
451 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
454 <ref type="managerEvent">ConfbridgeJoin</ref>
456 </managerEventInstance>
458 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
462 "CallerIDnum: %s\r\n"
463 "CallerIDname: %s\r\n",
464 ast_channel_name(chan),
465 ast_channel_uniqueid(chan),
467 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
468 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
472 static void send_start_record_event(const char *conf_name)
475 <managerEventInstance>
476 <synopsis>Raised when a conference recording starts.</synopsis>
478 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
481 <ref type="managerEvent">ConfbridgeStopRecord</ref>
482 <ref type="application">ConfBridge</ref>
484 </managerEventInstance>
487 manager_event(EVENT_FLAG_CALL, "ConfbridgeStartRecord", "Conference: %s\r\n", conf_name);
490 static void send_stop_record_event(const char *conf_name)
493 <managerEventInstance>
494 <synopsis>Raised when a conference recording stops.</synopsis>
496 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
499 <ref type="managerEvent">ConfbridgeStartRecord</ref>
501 </managerEventInstance>
503 manager_event(EVENT_FLAG_CALL, "ConfbridgeStopRecord", "Conference: %s\r\n", conf_name);
506 static void send_mute_event(struct ast_channel *chan, const char *conf_name)
509 <managerEventInstance>
510 <synopsis>Raised when a Confbridge participant mutes.</synopsis>
512 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
515 <ref type="managerEvent">ConfbridgeUnmute</ref>
516 <ref type="application">ConfBridge</ref>
518 </managerEventInstance>
520 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeMute",
524 "CallerIDnum: %s\r\n"
525 "CallerIDname: %s\r\n",
526 ast_channel_name(chan),
527 ast_channel_uniqueid(chan),
529 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
530 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
534 static void send_unmute_event(struct ast_channel *chan, const char *conf_name)
537 <managerEventInstance>
538 <synopsis>Raised when a Confbridge participant unmutes.</synopsis>
540 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
543 <ref type="managerEvent">ConfbridgeMute</ref>
545 </managerEventInstance>
547 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeUnmute",
551 "CallerIDnum: %s\r\n"
552 "CallerIDname: %s\r\n",
553 ast_channel_name(chan),
554 ast_channel_uniqueid(chan),
556 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
557 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
562 static struct ast_frame *rec_read(struct ast_channel *ast)
564 return &ast_null_frame;
566 static int rec_write(struct ast_channel *ast, struct ast_frame *f)
570 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
571 static struct ast_channel_tech record_tech = {
572 .type = "ConfBridgeRec",
573 .description = "Conference Bridge Recording Channel",
574 .requester = rec_request,
578 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
580 struct ast_channel *tmp;
581 struct ast_format fmt;
582 const char *conf_name = data;
583 if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
584 "ConfBridgeRecorder/conf-%s-uid-%d",
586 (int) ast_random()))) {
589 ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
590 ast_channel_tech_set(tmp, &record_tech);
591 ast_format_cap_add_all(ast_channel_nativeformats(tmp));
592 ast_format_copy(ast_channel_writeformat(tmp), &fmt);
593 ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
594 ast_format_copy(ast_channel_readformat(tmp), &fmt);
595 ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
599 static void *record_thread(void *obj)
601 struct conference_bridge *conference_bridge = obj;
602 struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
603 struct ast_channel *chan;
604 struct ast_str *filename = ast_str_alloca(PATH_MAX);
606 ast_mutex_lock(&conference_bridge->record_lock);
608 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
609 conference_bridge->record_thread = AST_PTHREADT_NULL;
610 ast_mutex_unlock(&conference_bridge->record_lock);
611 ao2_ref(conference_bridge, -1);
615 /* XXX If we get an EXIT right here, START will essentially be a no-op */
616 while (conference_bridge->record_state != CONF_RECORD_EXIT) {
617 if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
618 ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
622 ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
623 conference_bridge->name,
627 chan = ast_channel_ref(conference_bridge->record_chan);
629 pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
630 ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
632 ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
633 /* STOP has been called. Wait for either a START or an EXIT */
634 ast_cond_wait(&conference_bridge->record_cond, &conference_bridge->record_lock);
636 ast_mutex_unlock(&conference_bridge->record_lock);
637 ao2_ref(conference_bridge, -1);
641 /*! \brief Returns whether or not conference is being recorded.
642 * \param conference_bridge The bridge to check for recording
643 * \retval 1, conference is recording.
644 * \retval 0, conference is NOT recording.
646 static int conf_is_recording(struct conference_bridge *conference_bridge)
648 return conference_bridge->record_state == CONF_RECORD_START;
651 /*! \brief Stop recording a conference bridge
653 * \param conference_bridge The conference bridge on which to stop the recording
657 static int conf_stop_record(struct conference_bridge *conference_bridge)
659 struct ast_channel *chan;
660 if (conference_bridge->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference_bridge)) {
663 conference_bridge->record_state = CONF_RECORD_STOP;
664 chan = ast_channel_ref(conference_bridge->record_chan);
665 ast_bridge_remove(conference_bridge->bridge, chan);
666 ast_queue_frame(chan, &ast_null_frame);
667 chan = ast_channel_unref(chan);
668 ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
669 send_stop_record_event(conference_bridge->name);
676 * \brief Stops the confbridge recording thread.
678 * \note Must be called with the conference_bridge locked
680 static int conf_stop_record_thread(struct conference_bridge *conference_bridge)
682 if (conference_bridge->record_thread == AST_PTHREADT_NULL) {
685 conf_stop_record(conference_bridge);
687 ast_mutex_lock(&conference_bridge->record_lock);
688 conference_bridge->record_state = CONF_RECORD_EXIT;
689 ast_cond_signal(&conference_bridge->record_cond);
690 ast_mutex_unlock(&conference_bridge->record_lock);
692 pthread_join(conference_bridge->record_thread, NULL);
693 conference_bridge->record_thread = AST_PTHREADT_NULL;
695 /* this is the reference given to the channel during the channel alloc */
696 if (conference_bridge->record_chan) {
697 conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan);
703 /*! \brief Start recording the conference
705 * \note conference_bridge must be locked when calling this function
706 * \param conference_bridge The conference bridge to start recording
708 * \rteval non-zero failure
710 static int conf_start_record(struct conference_bridge *conference_bridge)
712 struct ast_format_cap *cap;
713 struct ast_format tmpfmt;
716 if (conference_bridge->record_state != CONF_RECORD_STOP) {
720 if (!pbx_findapp("MixMonitor")) {
721 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
725 if (!(cap = ast_format_cap_alloc_nolock())) {
729 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
731 if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) {
732 cap = ast_format_cap_destroy(cap);
736 cap = ast_format_cap_destroy(cap);
738 conference_bridge->record_state = CONF_RECORD_START;
739 ast_mutex_lock(&conference_bridge->record_lock);
740 ast_cond_signal(&conference_bridge->record_cond);
741 ast_mutex_unlock(&conference_bridge->record_lock);
742 ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
743 send_start_record_event(conference_bridge->name);
748 /*! \brief Start the recording thread on a conference bridge
750 * \param conference_bridge The conference bridge on which to start the recording thread
754 static int start_conf_record_thread(struct conference_bridge *conference_bridge)
756 ao2_ref(conference_bridge, +1); /* give the record thread a ref */
758 conf_start_record(conference_bridge);
760 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
761 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
762 ao2_ref(conference_bridge, -1); /* error so remove ref */
771 * \brief Complain if the given sound file does not exist.
773 * \param filename Sound file to check if exists.
775 * \retval non-zero if the file exists.
777 static int sound_file_exists(const char *filename)
779 if (ast_fileexists(filename, NULL, NULL)) {
782 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
787 * \brief Announce number of users in the conference bridge to the caller
789 * \param conference_bridge Conference bridge to peek at
790 * \param conference_bridge_user Optional Caller
792 * \note if caller is NULL, the announcment will be sent to all participants in the conference.
793 * \return Returns 0 on success, -1 if the user hung up
795 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
797 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
798 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
799 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
801 if (conference_bridge->activeusers <= 1) {
802 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
804 } else if (conference_bridge->activeusers == 2) {
805 if (conference_bridge_user) {
806 /* Eep, there is one other person */
807 if (ast_stream_and_wait(conference_bridge_user->chan,
813 play_sound_file(conference_bridge, only_one);
816 /* Alas multiple others in here */
817 if (conference_bridge_user) {
818 if (ast_stream_and_wait(conference_bridge_user->chan,
823 if (ast_say_number(conference_bridge_user->chan, conference_bridge->activeusers - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) {
826 if (ast_stream_and_wait(conference_bridge_user->chan,
831 } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
832 play_sound_file(conference_bridge, there_are);
833 play_sound_number(conference_bridge, conference_bridge->activeusers - 1);
834 play_sound_file(conference_bridge, other_in_party);
841 * \brief Play back an audio file to a channel
843 * \param cbu User to play audio prompt to
844 * \param filename Prompt to play
846 * \return Returns 0 on success, -1 if the user hung up
847 * \note Generally this should be called when the conference is unlocked to avoid blocking
848 * the entire conference while the sound is played. But don't unlock the conference bridge
849 * in the middle of a state transition.
851 static int play_prompt_to_user(struct conference_bridge_user *cbu, const char *filename)
853 return ast_stream_and_wait(cbu->chan, filename, "");
856 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
858 /* Right now, only marked users are automatically set as the single src of video.*/
863 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
865 struct conference_bridge_user *tmp_user = NULL;
866 ao2_lock(conference_bridge);
867 /* see if anyone is already the video src */
868 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
869 if (tmp_user->chan == chan) {
872 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
877 ao2_unlock(conference_bridge);
879 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
881 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
882 /* we joined and are video capable, we override anyone else that may have already been the video feed */
883 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
887 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
889 struct conference_bridge_user *tmp_user = NULL;
891 /* if this isn't a video source, nothing to update */
892 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
896 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
898 /* If in follow talker mode, make sure to restore this mode on the
899 * bridge when a source is removed. It is possible this channel was
900 * only set temporarily as a video source by an AMI or DTMF action. */
901 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
902 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
905 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
906 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
907 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
911 /* Make the next available marked user the video src. */
912 ao2_lock(conference_bridge);
913 AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) {
914 if (tmp_user->chan == chan) {
917 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
918 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
922 ao2_unlock(conference_bridge);
926 * \brief Destroy a conference bridge
928 * \param obj The conference bridge object
930 * \return Returns nothing
932 static void destroy_conference_bridge(void *obj)
934 struct conference_bridge *conference_bridge = obj;
936 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
938 if (conference_bridge->playback_chan) {
939 struct ast_channel *underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
940 if (underlying_channel) {
941 ast_hangup(underlying_channel);
943 ast_hangup(conference_bridge->playback_chan);
944 conference_bridge->playback_chan = NULL;
947 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
948 if (conference_bridge->bridge) {
949 ast_bridge_destroy(conference_bridge->bridge);
950 conference_bridge->bridge = NULL;
953 conf_bridge_profile_destroy(&conference_bridge->b_profile);
954 ast_cond_destroy(&conference_bridge->record_cond);
955 ast_mutex_destroy(&conference_bridge->record_lock);
956 ast_mutex_destroy(&conference_bridge->playback_lock);
959 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
961 * \param cbu The conference bridge user that is joining
965 static int handle_conf_user_join(struct conference_bridge_user *cbu)
967 conference_event_fn handler;
968 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
969 handler = cbu->conference_bridge->state->join_marked;
970 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
971 handler = cbu->conference_bridge->state->join_waitmarked;
973 handler = cbu->conference_bridge->state->join_unmarked;
976 ast_assert(handler != NULL);
979 conf_invalid_event_fn(cbu);
988 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
990 * \param cbu The conference bridge user that is leaving
994 static int handle_conf_user_leave(struct conference_bridge_user *cbu)
996 conference_event_fn handler;
997 if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) {
998 handler = cbu->conference_bridge->state->leave_marked;
999 } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) {
1000 handler = cbu->conference_bridge->state->leave_waitmarked;
1002 handler = cbu->conference_bridge->state->leave_unmarked;
1005 ast_assert(handler != NULL);
1008 /* This should never happen. If it does, though, it is bad. The user will not have been removed
1009 * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
1010 * Shouldn't happen, though. */
1011 conf_invalid_event_fn(cbu);
1020 void conf_moh_stop(struct conference_bridge_user *user)
1022 user->playing_moh = 0;
1023 if (!user->suspended_moh) {
1027 * Locking the ast_bridge here is the only way to hold off the
1028 * call to ast_bridge_join() in confbridge_exec() from
1029 * interfering with the bridge and MOH operations here.
1031 ast_bridge_lock(user->conference_bridge->bridge);
1034 * Temporarily suspend the user from the bridge so we have
1035 * control to stop MOH if needed.
1037 in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan);
1038 ast_moh_stop(user->chan);
1040 ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan);
1043 ast_bridge_unlock(user->conference_bridge->bridge);
1047 void conf_moh_start(struct conference_bridge_user *user)
1049 user->playing_moh = 1;
1050 if (!user->suspended_moh) {
1054 * Locking the ast_bridge here is the only way to hold off the
1055 * call to ast_bridge_join() in confbridge_exec() from
1056 * interfering with the bridge and MOH operations here.
1058 ast_bridge_lock(user->conference_bridge->bridge);
1061 * Temporarily suspend the user from the bridge so we have
1062 * control to start MOH if needed.
1064 in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan);
1065 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1067 ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan);
1070 ast_bridge_unlock(user->conference_bridge->bridge);
1076 * \brief Unsuspend MOH for the conference user.
1078 * \param user Conference user to unsuspend MOH on.
1082 static void conf_moh_unsuspend(struct conference_bridge_user *user)
1084 ao2_lock(user->conference_bridge);
1085 if (--user->suspended_moh == 0 && user->playing_moh) {
1086 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1088 ao2_unlock(user->conference_bridge);
1093 * \brief Suspend MOH for the conference user.
1095 * \param user Conference user to suspend MOH on.
1099 static void conf_moh_suspend(struct conference_bridge_user *user)
1101 ao2_lock(user->conference_bridge);
1102 if (user->suspended_moh++ == 0 && user->playing_moh) {
1103 ast_moh_stop(user->chan);
1105 ao2_unlock(user->conference_bridge);
1108 int conf_handle_first_marked_common(struct conference_bridge_user *cbu)
1110 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))) {
1116 int conf_handle_inactive_waitmarked(struct conference_bridge_user *cbu)
1118 /* If we have not been quieted play back that they are waiting for the leader */
1119 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu,
1120 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, cbu->b_profile.sounds))) {
1121 /* user hungup while the sound was playing */
1127 int conf_handle_only_unmarked(struct conference_bridge_user *cbu)
1129 /* If audio prompts have not been quieted or this prompt quieted play it on out */
1130 if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
1131 if (play_prompt_to_user(cbu,
1132 conf_get_sound(CONF_SOUND_ONLY_PERSON, cbu->b_profile.sounds))) {
1133 /* user hungup while the sound was playing */
1140 int conf_add_post_join_action(struct conference_bridge_user *cbu, int (*func)(struct conference_bridge_user *cbu))
1142 struct post_join_action *action;
1143 if (!(action = ast_calloc(1, sizeof(*action)))) {
1146 action->func = func;
1147 AST_LIST_INSERT_TAIL(&cbu->post_join_list, action, list);
1152 void conf_handle_first_join(struct conference_bridge *conference_bridge)
1154 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name);
1157 void conf_handle_second_active(struct conference_bridge *conference_bridge)
1159 /* If we are the second participant we may need to stop music on hold on the first */
1160 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->active_list);
1162 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD)) {
1163 conf_moh_stop(first_participant);
1165 if (!ast_test_flag(&first_participant->u_profile, USER_OPT_STARTMUTED)) {
1166 first_participant->features.mute = 0;
1170 void conf_ended(struct conference_bridge *conference_bridge)
1172 /* Called with a reference to conference_bridge */
1173 ao2_unlink(conference_bridges, conference_bridge);
1174 send_conf_end_event(conference_bridge->name);
1175 conf_stop_record_thread(conference_bridge);
1179 * \brief Join a conference bridge
1181 * \param name The conference name
1182 * \param conference_bridge_user Conference bridge user structure
1184 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1186 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
1188 struct conference_bridge *conference_bridge = NULL;
1189 struct post_join_action *action;
1190 struct conference_bridge tmp;
1191 int max_members_reached = 0;
1193 ast_copy_string(tmp.name, name, sizeof(tmp.name));
1195 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
1196 ao2_lock(conference_bridges);
1198 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
1200 /* Attempt to find an existing conference bridge */
1201 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1203 if (conference_bridge && conference_bridge->b_profile.max_members) {
1204 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->activeusers ? 0 : 1;
1207 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1208 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
1209 ao2_unlock(conference_bridges);
1210 ao2_ref(conference_bridge, -1);
1211 ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", name);
1212 ast_stream_and_wait(conference_bridge_user->chan,
1213 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
1218 /* If no conference bridge was found see if we can create one */
1219 if (!conference_bridge) {
1220 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1221 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
1222 ao2_unlock(conference_bridges);
1223 ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", name);
1227 /* Setup lock for playback channel */
1228 ast_mutex_init(&conference_bridge->playback_lock);
1230 /* Setup lock for the record channel */
1231 ast_mutex_init(&conference_bridge->record_lock);
1232 ast_cond_init(&conference_bridge->record_cond, NULL);
1234 /* Setup conference bridge parameters */
1235 conference_bridge->record_thread = AST_PTHREADT_NULL;
1236 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
1237 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
1239 /* Create an actual bridge that will do the audio mixing */
1240 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
1241 ao2_ref(conference_bridge, -1);
1242 conference_bridge = NULL;
1243 ao2_unlock(conference_bridges);
1244 ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", name);
1248 /* Set the internal sample rate on the bridge from the bridge profile */
1249 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
1250 /* Set the internal mixing interval on the bridge from the bridge profile */
1251 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
1253 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1254 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
1257 /* Link it into the conference bridges container */
1258 if (!ao2_link(conference_bridges, conference_bridge)) {
1259 ao2_ref(conference_bridge, -1);
1260 conference_bridge = NULL;
1261 ao2_unlock(conference_bridges);
1263 "Conference '%s' could not be added to the conferences list.\n", name);
1267 /* Set the initial state to EMPTY */
1268 conference_bridge->state = CONF_STATE_EMPTY;
1270 conference_bridge->record_state = CONF_RECORD_STOP;
1271 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
1272 ao2_lock(conference_bridge);
1273 start_conf_record_thread(conference_bridge);
1274 ao2_unlock(conference_bridge);
1277 send_conf_start_event(conference_bridge->name);
1278 ast_debug(1, "Created conference '%s' and linked to container.\n", name);
1281 ao2_unlock(conference_bridges);
1283 /* Setup conference bridge user parameters */
1284 conference_bridge_user->conference_bridge = conference_bridge;
1286 ao2_lock(conference_bridge);
1289 * Suspend any MOH until the user actually joins the bridge of
1290 * the conference. This way any pre-join file playback does not
1291 * need to worry about MOH.
1293 conference_bridge_user->suspended_moh = 1;
1295 if (handle_conf_user_join(conference_bridge_user)) {
1296 /* Invalid event, nothing was done, so we don't want to process a leave. */
1297 ao2_unlock(conference_bridge);
1298 ao2_ref(conference_bridge, -1);
1302 if (ast_check_hangup(conference_bridge_user->chan)) {
1303 ao2_unlock(conference_bridge);
1304 leave_conference(conference_bridge_user);
1308 ao2_unlock(conference_bridge);
1310 /* If an announcement is to be played play it */
1311 if (!ast_strlen_zero(conference_bridge_user->u_profile.announcement)) {
1312 if (play_prompt_to_user(conference_bridge_user,
1313 conference_bridge_user->u_profile.announcement)) {
1314 leave_conference(conference_bridge_user);
1319 /* Announce number of users if need be */
1320 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
1321 if (announce_user_count(conference_bridge, conference_bridge_user)) {
1322 leave_conference(conference_bridge_user);
1327 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
1328 (conference_bridge->activeusers > conference_bridge_user->u_profile.announce_user_count_all_after)) {
1332 * We have to autoservice the new user because he has not quite
1333 * joined the conference yet.
1335 ast_autoservice_start(conference_bridge_user->chan);
1336 user_count_res = announce_user_count(conference_bridge, NULL);
1337 ast_autoservice_stop(conference_bridge_user->chan);
1338 if (user_count_res) {
1339 leave_conference(conference_bridge_user);
1344 /* Handle post-join actions */
1345 while ((action = AST_LIST_REMOVE_HEAD(&conference_bridge_user->post_join_list, list))) {
1346 action->func(conference_bridge_user);
1350 return conference_bridge;
1354 * \brief Leave a conference
1356 * \param user The conference user
1358 static void leave_conference(struct conference_bridge_user *user)
1360 struct post_join_action *action;
1362 ao2_lock(user->conference_bridge);
1363 handle_conf_user_leave(user);
1364 ao2_unlock(user->conference_bridge);
1366 /* Discard any post-join actions */
1367 while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
1371 /* Done mucking with the conference, huzzah */
1372 ao2_ref(user->conference_bridge, -1);
1373 user->conference_bridge = NULL;
1378 * \brief allocates playback chan on a channel
1379 * \pre expects conference to be locked before calling this function
1381 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
1384 struct ast_format_cap *cap;
1385 struct ast_format tmpfmt;
1387 if (conference_bridge->playback_chan) {
1390 if (!(cap = ast_format_cap_alloc_nolock())) {
1393 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
1394 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
1395 cap = ast_format_cap_destroy(cap);
1398 cap = ast_format_cap_destroy(cap);
1400 ast_channel_internal_bridge_set(conference_bridge->playback_chan, conference_bridge->bridge);
1402 if (ast_call(conference_bridge->playback_chan, "", 0)) {
1403 ast_hangup(conference_bridge->playback_chan);
1404 conference_bridge->playback_chan = NULL;
1408 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
1412 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
1414 struct ast_channel *underlying_channel;
1416 /* Do not waste resources trying to play files that do not exist */
1417 if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) {
1421 ast_mutex_lock(&conference_bridge->playback_lock);
1422 if (!(conference_bridge->playback_chan)) {
1423 if (alloc_playback_chan(conference_bridge)) {
1424 ast_mutex_unlock(&conference_bridge->playback_lock);
1427 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1429 /* Channel was already available so we just need to add it back into the bridge */
1430 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1431 if (ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0)) {
1432 ast_mutex_unlock(&conference_bridge->playback_lock);
1437 /* The channel is all under our control, in goes the prompt */
1438 if (!ast_strlen_zero(filename)) {
1439 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
1440 } else if (say_number >= 0) {
1441 ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL);
1444 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge);
1445 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
1447 ast_mutex_unlock(&conference_bridge->playback_lock);
1452 int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
1454 return play_sound_helper(conference_bridge, filename, -1);
1458 * \brief Play number into the conference bridge
1460 * \param conference_bridge The conference bridge to say the number into
1461 * \param say_number number to say
1464 * \retval -1 failure
1466 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
1468 return play_sound_helper(conference_bridge, NULL, say_number);
1471 static void conf_handle_talker_destructor(void *pvt_data)
1476 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
1478 char *conf_name = pvt_data;
1481 switch (bridge_channel->state) {
1482 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
1485 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
1489 return; /* uhh this shouldn't happen, but bail if it does. */
1492 /* notify AMI someone is has either started or stopped talking */
1494 <managerEventInstance>
1495 <synopsis>Raised when a conference participant has started or stopped talking.</synopsis>
1497 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
1498 <parameter name="TalkingStatus">
1505 </managerEventInstance>
1507 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
1510 "Conference: %s\r\n"
1511 "TalkingStatus: %s\r\n",
1512 ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
1515 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
1517 char pin_guess[MAX_PIN+1] = { 0, };
1518 const char *pin = conference_bridge_user->u_profile.pin;
1519 char *tmp = pin_guess;
1521 unsigned int len = MAX_PIN ;
1523 /* give them three tries to get the pin right */
1524 for (i = 0; i < 3; i++) {
1525 if (ast_app_getdata(chan,
1526 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
1527 tmp, len, 0) >= 0) {
1528 if (!strcasecmp(pin, pin_guess)) {
1532 ast_streamfile(chan,
1533 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
1534 ast_channel_language(chan));
1535 res = ast_waitstream(chan, AST_DIGIT_ANY);
1537 /* Account for digit already read during ivalid pin playback
1538 * resetting pin buf. */
1540 pin_guess[1] = '\0';
1541 tmp = pin_guess + 1;
1544 /* reset pin buf as empty buffer. */
1552 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
1554 char destdir[PATH_MAX];
1558 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1560 if (ast_mkdir(destdir, 0777) != 0) {
1561 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1564 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1565 "%s/confbridge-name-%s-%s", destdir,
1566 conf_name, ast_channel_uniqueid(user->chan));
1568 res = ast_play_and_record(user->chan,
1570 user->name_rec_location,
1575 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1580 user->name_rec_location[0] = '\0';
1586 /*! \brief The ConfBridge application */
1587 static int confbridge_exec(struct ast_channel *chan, const char *data)
1589 int res = 0, volume_adjustments[2];
1592 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
1593 const char *u_profile_name = DEFAULT_USER_PROFILE;
1594 struct conference_bridge *conference_bridge = NULL;
1595 struct conference_bridge_user conference_bridge_user = {
1597 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1598 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1599 .tech_args.drop_silence = 0,
1601 AST_DECLARE_APP_ARGS(args,
1602 AST_APP_ARG(conf_name);
1603 AST_APP_ARG(b_profile_name);
1604 AST_APP_ARG(u_profile_name);
1605 AST_APP_ARG(menu_name);
1607 ast_bridge_features_init(&conference_bridge_user.features);
1609 if (ast_channel_state(chan) != AST_STATE_UP) {
1613 if (ast_strlen_zero(data)) {
1614 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1615 res = -1; /* invalid PIN */
1616 goto confbridge_cleanup;
1619 /* We need to make a copy of the input string if we are going to modify it! */
1620 parse = ast_strdupa(data);
1622 AST_STANDARD_APP_ARGS(args, parse);
1624 /* bridge profile name */
1625 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1626 b_profile_name = args.b_profile_name;
1628 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
1629 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
1631 goto confbridge_cleanup;
1634 /* user profile name */
1635 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1636 u_profile_name = args.u_profile_name;
1638 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
1639 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
1641 goto confbridge_cleanup;
1644 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
1646 /* ask for a PIN immediately after finding user profile. This has to be
1647 * prompted for requardless of quiet setting. */
1648 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
1649 if (conf_get_pin(chan, &conference_bridge_user)) {
1650 res = -1; /* invalid PIN */
1651 goto confbridge_cleanup;
1655 /* See if we need them to record a intro name */
1656 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
1657 conf_rec_name(&conference_bridge_user, args.conf_name);
1661 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
1662 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
1663 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
1664 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
1666 res = -1; /* invalid PIN */
1667 goto confbridge_cleanup;
1671 /* Set if DTMF should pass through for this user or not */
1672 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
1673 conference_bridge_user.features.dtmf_passthrough = 1;
1676 /* Set dsp threshold values if present */
1677 if (conference_bridge_user.u_profile.talking_threshold) {
1678 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
1680 if (conference_bridge_user.u_profile.silence_threshold) {
1681 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
1684 /* Set a talker indicate call back if talking detection is requested */
1685 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
1686 char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
1688 res = -1; /* invalid PIN */
1689 goto confbridge_cleanup;
1691 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
1692 conf_handle_talker_cb,
1693 conf_handle_talker_destructor,
1697 /* Look for a conference bridge matching the provided name */
1698 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
1699 res = -1; /* invalid PIN */
1700 goto confbridge_cleanup;
1703 /* Keep a copy of volume adjustments so we can restore them later if need be */
1704 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1705 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1707 /* If the caller should be joined already muted, make it so */
1708 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
1709 conference_bridge_user.features.mute = 1;
1712 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
1713 conference_bridge_user.tech_args.drop_silence = 1;
1716 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
1718 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1720 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1724 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
1726 /* Reduce background noise from each participant */
1727 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1728 ast_free(mod_speex);
1729 ast_func_write(chan, "DENOISE(rx)", "on");
1733 /* if this user has a intro, play it before entering */
1734 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1735 ast_autoservice_start(chan);
1736 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1737 play_sound_file(conference_bridge,
1738 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
1739 ast_autoservice_stop(chan);
1742 /* Play the Join sound to both the conference and the user entering. */
1744 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
1746 ast_stream_and_wait(chan, join_sound, "");
1747 ast_autoservice_start(chan);
1748 play_sound_file(conference_bridge, join_sound);
1749 ast_autoservice_stop(chan);
1752 /* See if we need to automatically set this user as a video source or not */
1753 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
1755 conf_moh_unsuspend(&conference_bridge_user);
1757 /* Join our conference bridge for real */
1758 send_join_event(conference_bridge_user.chan, conference_bridge->name);
1759 ast_bridge_join(conference_bridge->bridge,
1762 &conference_bridge_user.features,
1763 &conference_bridge_user.tech_args);
1764 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
1766 /* if we're shutting down, don't attempt to do further processing */
1767 if (ast_shutting_down()) {
1768 leave_conference(&conference_bridge_user);
1769 conference_bridge = NULL;
1770 goto confbridge_cleanup;
1773 /* If this user was a video source, we need to clean up and possibly pick a new source. */
1774 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
1776 /* if this user has a intro, play it when leaving */
1777 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1778 ast_autoservice_start(chan);
1779 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1780 play_sound_file(conference_bridge,
1781 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
1782 ast_autoservice_stop(chan);
1785 /* play the leave sound */
1787 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
1788 ast_autoservice_start(chan);
1789 play_sound_file(conference_bridge, leave_sound);
1790 ast_autoservice_stop(chan);
1793 /* Easy as pie, depart this channel from the conference bridge */
1794 leave_conference(&conference_bridge_user);
1795 conference_bridge = NULL;
1797 /* If the user was kicked from the conference play back the audio prompt for it */
1798 if (!quiet && conference_bridge_user.kicked) {
1799 res = ast_stream_and_wait(chan,
1800 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
1804 /* Restore volume adjustments to previous values in case they were changed */
1805 if (volume_adjustments[0]) {
1806 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1808 if (volume_adjustments[1]) {
1809 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1812 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1813 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
1817 ast_bridge_features_cleanup(&conference_bridge_user.features);
1818 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
1822 static int action_toggle_mute(struct conference_bridge *conference_bridge,
1823 struct conference_bridge_user *conference_bridge_user,
1824 struct ast_channel *chan)
1826 /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
1827 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
1828 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
1829 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));
1830 if (conference_bridge_user->features.mute) {
1831 send_mute_event(chan, conference_bridge->name);
1833 send_unmute_event(chan, conference_bridge->name);
1836 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
1837 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
1838 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
1842 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1844 struct conference_bridge_user *participant = NULL;
1845 const char *sound_to_play;
1847 ao2_lock(conference_bridge);
1849 /* If already muted, then unmute */
1850 conference_bridge->muted = conference_bridge->muted ? 0 : 1;
1851 sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
1852 conference_bridge_user->b_profile.sounds);
1854 AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) {
1855 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
1856 participant->features.mute = conference_bridge->muted;
1860 ao2_unlock(conference_bridge);
1862 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
1863 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
1865 /* Announce to the group that all participants are muted */
1866 ast_autoservice_start(conference_bridge_user->chan);
1867 play_sound_helper(conference_bridge, sound_to_play, 0);
1868 ast_autoservice_stop(conference_bridge_user->chan);
1873 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1875 char *file_copy = ast_strdupa(playback_file);
1878 while ((file = strsep(&file_copy, "&"))) {
1879 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1880 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1887 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
1888 struct conference_bridge_user *conference_bridge_user,
1889 struct ast_bridge_channel *bridge_channel,
1890 struct conf_menu *menu,
1891 const char *playback_file,
1892 const char *cur_dtmf,
1897 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1898 struct conf_menu_entry new_menu_entry = { { 0, }, };
1899 char *file_copy = ast_strdupa(playback_file);
1902 while ((file = strsep(&file_copy, "&"))) {
1903 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
1904 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1908 /* now wait for more digits. */
1909 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1910 /* streaming finished and no DTMF was entered */
1912 } else if (digit == -1) {
1916 break; /* dtmf was entered */
1920 /* streaming finished on all files and no DTMF was entered */
1923 ast_stopstream(bridge_channel->chan);
1925 /* If we get here, then DTMF has been entered, This means no
1926 * additional prompts should be played for this menu entry */
1929 /* If a digit was pressed during the payback, update
1930 * the dtmf string and look for a new menu entry in the
1932 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1933 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1934 dtmf[i] = cur_dtmf[i];
1936 dtmf[i] = (char) digit;
1942 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1943 * If this is the case, no new DTMF sequence should be looked for. */
1948 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1949 execute_menu_entry(conference_bridge,
1950 conference_bridge_user,
1952 &new_menu_entry, menu);
1953 conf_menu_entry_destroy(&new_menu_entry);
1958 static int action_kick_last(struct conference_bridge *conference_bridge,
1959 struct ast_bridge_channel *bridge_channel,
1960 struct conference_bridge_user *conference_bridge_user)
1962 struct conference_bridge_user *last_participant = NULL;
1963 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1966 ast_stream_and_wait(bridge_channel->chan,
1967 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1969 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1970 ast_channel_name(bridge_channel->chan),
1971 conference_bridge->name);
1975 ao2_lock(conference_bridge);
1976 if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user)
1977 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
1978 ao2_unlock(conference_bridge);
1979 ast_stream_and_wait(bridge_channel->chan,
1980 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1982 } else if (last_participant) {
1983 last_participant->kicked = 1;
1984 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
1985 ao2_unlock(conference_bridge);
1990 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1992 struct ast_pbx_args args;
1993 struct ast_pbx *pbx;
1999 memset(&args, 0, sizeof(args));
2000 args.no_hangup_chan = 1;
2002 ast_channel_lock(bridge_channel->chan);
2005 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
2006 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
2007 priority = ast_channel_priority(bridge_channel->chan);
2008 pbx = ast_channel_pbx(bridge_channel->chan);
2009 ast_channel_pbx_set(bridge_channel->chan, NULL);
2012 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
2013 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
2014 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
2016 ast_channel_unlock(bridge_channel->chan);
2019 res = ast_pbx_run_args(bridge_channel->chan, &args);
2022 ast_channel_lock(bridge_channel->chan);
2024 ast_channel_exten_set(bridge_channel->chan, exten);
2025 ast_channel_context_set(bridge_channel->chan, context);
2026 ast_channel_priority_set(bridge_channel->chan, priority);
2027 ast_channel_pbx_set(bridge_channel->chan, pbx);
2029 ast_channel_unlock(bridge_channel->chan);
2034 static int execute_menu_entry(struct conference_bridge *conference_bridge,
2035 struct conference_bridge_user *conference_bridge_user,
2036 struct ast_bridge_channel *bridge_channel,
2037 struct conf_menu_entry *menu_entry,
2038 struct conf_menu *menu)
2040 struct conf_menu_action *menu_action;
2041 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
2042 int stop_prompts = 0;
2045 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
2046 switch (menu_action->id) {
2047 case MENU_ACTION_TOGGLE_MUTE:
2048 res |= action_toggle_mute(conference_bridge,
2049 conference_bridge_user,
2050 bridge_channel->chan);
2052 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
2056 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
2058 case MENU_ACTION_PARTICIPANT_COUNT:
2059 announce_user_count(conference_bridge, conference_bridge_user);
2061 case MENU_ACTION_PLAYBACK:
2062 if (!stop_prompts) {
2063 res |= action_playback(bridge_channel, menu_action->data.playback_file);
2066 case MENU_ACTION_RESET_LISTENING:
2067 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
2069 case MENU_ACTION_RESET_TALKING:
2070 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
2072 case MENU_ACTION_INCREASE_LISTENING:
2073 ast_audiohook_volume_adjust(conference_bridge_user->chan,
2074 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
2076 case MENU_ACTION_DECREASE_LISTENING:
2077 ast_audiohook_volume_adjust(conference_bridge_user->chan,
2078 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
2080 case MENU_ACTION_INCREASE_TALKING:
2081 ast_audiohook_volume_adjust(conference_bridge_user->chan,
2082 AST_AUDIOHOOK_DIRECTION_READ, 1);
2084 case MENU_ACTION_DECREASE_TALKING:
2085 ast_audiohook_volume_adjust(conference_bridge_user->chan,
2086 AST_AUDIOHOOK_DIRECTION_READ, -1);
2088 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
2089 if (!(stop_prompts)) {
2090 res |= action_playback_and_continue(conference_bridge,
2091 conference_bridge_user,
2094 menu_action->data.playback_file,
2099 case MENU_ACTION_DIALPLAN_EXEC:
2100 res |= action_dialplan_exec(bridge_channel, menu_action);
2102 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
2106 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
2107 res |= ast_stream_and_wait(bridge_channel->chan,
2108 (conference_bridge->locked ?
2109 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
2110 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
2114 case MENU_ACTION_ADMIN_KICK_LAST:
2115 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
2117 case MENU_ACTION_LEAVE:
2118 ao2_lock(conference_bridge);
2119 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
2120 ao2_unlock(conference_bridge);
2122 case MENU_ACTION_NOOP:
2124 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
2125 ao2_lock(conference_bridge);
2126 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
2127 ao2_unlock(conference_bridge);
2129 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
2130 handle_video_on_exit(conference_bridge, bridge_channel->chan);
2137 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
2138 struct conference_bridge_user *conference_bridge_user,
2139 struct conf_menu_entry *menu_entry,
2140 struct conf_menu *menu)
2142 /* See if music on hold is playing */
2143 conf_moh_suspend(conference_bridge_user);
2145 /* execute the list of actions associated with this menu entry */
2146 execute_menu_entry(conference_bridge_user->conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
2148 /* See if music on hold needs to be started back up again */
2149 conf_moh_unsuspend(conference_bridge_user);
2154 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
2157 struct conference_bridge *bridge = NULL;
2159 int wordlen = strlen(word);
2160 struct ao2_iterator iter;
2162 iter = ao2_iterator_init(conference_bridges, 0);
2163 while ((bridge = ao2_iterator_next(&iter))) {
2164 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
2165 res = ast_strdup(bridge->name);
2166 ao2_ref(bridge, -1);
2169 ao2_ref(bridge, -1);
2171 ao2_iterator_destroy(&iter);
2176 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2178 struct conference_bridge *bridge = NULL;
2179 struct conference_bridge tmp;
2180 struct conference_bridge_user *participant = NULL;
2184 e->command = "confbridge kick";
2186 "Usage: confbridge kick <conference> <channel>\n"
2187 " Kicks a channel out of the conference bridge.\n";
2191 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2195 return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
2202 return CLI_SHOWUSAGE;
2205 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2206 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2208 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2212 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2213 if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
2218 ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
2219 participant->kicked = 1;
2220 ast_bridge_remove(bridge->bridge, participant->chan);
2223 ao2_ref(bridge, -1);
2227 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct conference_bridge_user *participant)
2229 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
2230 ast_cli(a->fd, "%-17s", participant->u_profile.name);
2231 ast_cli(a->fd, "%-17s", participant->b_profile.name);
2232 ast_cli(a->fd, "%-17s", participant->menu_name);
2233 ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
2234 ast_cli(a->fd, "\n");
2237 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2239 struct conference_bridge *bridge;
2243 e->command = "confbridge list";
2245 "Usage: confbridge list [<name>]\n"
2246 " Lists all currently active conference bridges.\n";
2250 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2256 struct ao2_iterator iter;
2258 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
2259 ast_cli(a->fd, "================================ ====== ====== ========\n");
2260 iter = ao2_iterator_init(conference_bridges, 0);
2261 while ((bridge = ao2_iterator_next(&iter))) {
2262 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers + bridge->waitingusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
2263 ao2_ref(bridge, -1);
2265 ao2_iterator_destroy(&iter);
2270 struct conference_bridge_user *participant;
2271 struct conference_bridge tmp;
2273 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2274 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2276 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2279 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
2280 ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
2282 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2283 handle_cli_confbridge_list_item(a, participant);
2285 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
2286 handle_cli_confbridge_list_item(a, participant);
2289 ao2_ref(bridge, -1);
2293 return CLI_SHOWUSAGE;
2297 * \brief finds a conference by name and locks/unlocks.
2300 * \retval -1 conference not found
2302 static int generic_lock_unlock_helper(int lock, const char *conference)
2304 struct conference_bridge *bridge = NULL;
2305 struct conference_bridge tmp;
2308 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2309 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2314 bridge->locked = lock;
2315 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
2317 ao2_ref(bridge, -1);
2323 * \brief finds a conference user by channel name and mutes/unmutes them.
2326 * \retval -1 conference not found
2327 * \retval -2 user not found
2329 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
2331 struct conference_bridge *bridge = NULL;
2332 struct conference_bridge tmp;
2333 struct conference_bridge_user *participant = NULL;
2335 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2336 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2341 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2342 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
2347 participant->features.mute = mute;
2348 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));
2353 ao2_ref(bridge, -1);
2358 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2360 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2363 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2365 } else if (res == -2) {
2366 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2369 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2373 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2377 e->command = "confbridge mute";
2379 "Usage: confbridge mute <conference> <channel>\n";
2383 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2388 return CLI_SHOWUSAGE;
2391 cli_mute_unmute_helper(1, a);
2396 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2400 e->command = "confbridge unmute";
2402 "Usage: confbridge unmute <conference> <channel>\n";
2406 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2411 return CLI_SHOWUSAGE;
2414 cli_mute_unmute_helper(0, a);
2419 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2423 e->command = "confbridge lock";
2425 "Usage: confbridge lock <conference>\n";
2429 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2434 return CLI_SHOWUSAGE;
2436 if (generic_lock_unlock_helper(1, a->argv[2])) {
2437 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2439 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2444 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2448 e->command = "confbridge unlock";
2450 "Usage: confbridge unlock <conference>\n";
2454 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2459 return CLI_SHOWUSAGE;
2461 if (generic_lock_unlock_helper(0, a->argv[2])) {
2462 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2464 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2469 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2471 const char *rec_file = NULL;
2472 struct conference_bridge *bridge = NULL;
2473 struct conference_bridge tmp;
2477 e->command = "confbridge record start";
2479 "Usage: confbridge record start <conference> <file>\n"
2480 " <file> is optional, Otherwise the bridge profile\n"
2481 " record file will be used. If the bridge profile\n"
2482 " has no record file specified, a file will automatically\n"
2483 " be generated in the monitor directory\n";
2487 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2492 return CLI_SHOWUSAGE;
2495 rec_file = a->argv[4];
2498 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2499 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2501 ast_cli(a->fd, "Conference not found.\n");
2505 if (conf_is_recording(bridge)) {
2506 ast_cli(a->fd, "Conference is already being recorded.\n");
2508 ao2_ref(bridge, -1);
2511 if (!ast_strlen_zero(rec_file)) {
2512 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2515 if (start_conf_record_thread(bridge)) {
2516 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2518 ao2_ref(bridge, -1);
2523 ast_cli(a->fd, "Recording started\n");
2524 ao2_ref(bridge, -1);
2528 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2530 struct conference_bridge *bridge = NULL;
2531 struct conference_bridge tmp;
2536 e->command = "confbridge record stop";
2538 "Usage: confbridge record stop <conference>\n";
2542 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2547 return CLI_SHOWUSAGE;
2550 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2551 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2553 ast_cli(a->fd, "Conference not found.\n");
2557 ret = conf_stop_record(bridge);
2559 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
2560 ao2_ref(bridge, -1);
2564 static struct ast_cli_entry cli_confbridge[] = {
2565 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2566 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2567 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2568 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
2569 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2570 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2571 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2572 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2574 static struct ast_custom_function confbridge_function = {
2575 .name = "CONFBRIDGE",
2576 .write = func_confbridge_helper,
2579 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2580 static struct ast_custom_function confbridge_info_function = {
2581 .name = "CONFBRIDGE_INFO",
2582 .read = func_confbridge_info,
2585 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct conference_bridge *bridge, struct conference_bridge_user *participant)
2588 "Event: ConfbridgeList\r\n"
2590 "Conference: %s\r\n"
2591 "CallerIDNum: %s\r\n"
2592 "CallerIDName: %s\r\n"
2595 "MarkedUser: %s\r\n"
2599 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
2600 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
2601 ast_channel_name(participant->chan),
2602 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2603 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
2606 static int action_confbridgelist(struct mansession *s, const struct message *m)
2608 const char *actionid = astman_get_header(m, "ActionID");
2609 const char *conference = astman_get_header(m, "Conference");
2610 struct conference_bridge_user *participant = NULL;
2611 struct conference_bridge *bridge = NULL;
2612 struct conference_bridge tmp;
2613 char id_text[80] = "";
2616 if (!ast_strlen_zero(actionid)) {
2617 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2619 if (ast_strlen_zero(conference)) {
2620 astman_send_error(s, m, "No Conference name provided.");
2623 if (!ao2_container_count(conference_bridges)) {
2624 astman_send_error(s, m, "No active conferences.");
2627 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2628 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2630 astman_send_error(s, m, "No Conference by that name found.");
2634 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2637 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2639 action_confbridgelist_item(s, id_text, bridge, participant);
2641 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
2643 action_confbridgelist_item(s, id_text, bridge, participant);
2646 ao2_ref(bridge, -1);
2649 "Event: ConfbridgeListComplete\r\n"
2650 "EventList: Complete\r\n"
2653 "\r\n", total, id_text);
2658 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2660 const char *actionid = astman_get_header(m, "ActionID");
2661 struct conference_bridge *bridge = NULL;
2662 struct ao2_iterator iter;
2663 char id_text[512] = "";
2666 if (!ast_strlen_zero(actionid)) {
2667 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2670 if (!ao2_container_count(conference_bridges)) {
2671 astman_send_error(s, m, "No active conferences.");
2675 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2677 /* Traverse the conference list */
2678 iter = ao2_iterator_init(conference_bridges, 0);
2679 while ((bridge = ao2_iterator_next(&iter))) {
2684 "Event: ConfbridgeListRooms\r\n"
2686 "Conference: %s\r\n"
2693 bridge->activeusers + bridge->waitingusers,
2694 bridge->markedusers,
2695 bridge->locked ? "Yes" : "No");
2698 ao2_ref(bridge, -1);
2700 ao2_iterator_destroy(&iter);
2702 /* Send final confirmation */
2704 "Event: ConfbridgeListRoomsComplete\r\n"
2705 "EventList: Complete\r\n"
2708 "\r\n", totalitems, id_text);
2712 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2714 const char *conference = astman_get_header(m, "Conference");
2715 const char *channel = astman_get_header(m, "Channel");
2718 if (ast_strlen_zero(conference)) {
2719 astman_send_error(s, m, "No Conference name provided.");
2722 if (ast_strlen_zero(channel)) {
2723 astman_send_error(s, m, "No channel name provided.");
2726 if (!ao2_container_count(conference_bridges)) {
2727 astman_send_error(s, m, "No active conferences.");
2731 res = generic_mute_unmute_helper(mute, conference, channel);
2734 astman_send_error(s, m, "No Conference by that name found.");
2736 } else if (res == -2) {
2737 astman_send_error(s, m, "No Channel by that name found in Conference.");
2741 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2745 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2747 return action_mute_unmute_helper(s, m, 0);
2749 static int action_confbridgemute(struct mansession *s, const struct message *m)
2751 return action_mute_unmute_helper(s, m, 1);
2754 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2756 const char *conference = astman_get_header(m, "Conference");
2759 if (ast_strlen_zero(conference)) {
2760 astman_send_error(s, m, "No Conference name provided.");
2763 if (!ao2_container_count(conference_bridges)) {
2764 astman_send_error(s, m, "No active conferences.");
2767 if ((res = generic_lock_unlock_helper(lock, conference))) {
2768 astman_send_error(s, m, "No Conference by that name found.");
2771 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2774 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2776 return action_lock_unlock_helper(s, m, 0);
2778 static int action_confbridgelock(struct mansession *s, const struct message *m)
2780 return action_lock_unlock_helper(s, m, 1);
2783 static int action_confbridgekick(struct mansession *s, const struct message *m)
2785 const char *conference = astman_get_header(m, "Conference");
2786 const char *channel = astman_get_header(m, "Channel");
2787 struct conference_bridge_user *participant = NULL;
2788 struct conference_bridge *bridge = NULL;
2789 struct conference_bridge tmp;
2792 if (ast_strlen_zero(conference)) {
2793 astman_send_error(s, m, "No Conference name provided.");
2796 if (!ao2_container_count(conference_bridges)) {
2797 astman_send_error(s, m, "No active conferences.");
2800 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2801 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2803 astman_send_error(s, m, "No Conference by that name found.");
2808 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2809 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
2810 participant->kicked = 1;
2811 ast_bridge_remove(bridge->bridge, participant->chan);
2817 ao2_ref(bridge, -1);
2820 astman_send_ack(s, m, "User kicked");
2822 astman_send_error(s, m, "No Channel by that name found in Conference.");
2827 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2829 const char *conference = astman_get_header(m, "Conference");
2830 const char *recordfile = astman_get_header(m, "RecordFile");
2831 struct conference_bridge *bridge = NULL;
2832 struct conference_bridge tmp;
2834 if (ast_strlen_zero(conference)) {
2835 astman_send_error(s, m, "No Conference name provided.");
2838 if (!ao2_container_count(conference_bridges)) {
2839 astman_send_error(s, m, "No active conferences.");
2843 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2844 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2846 astman_send_error(s, m, "No Conference by that name found.");
2851 if (conf_is_recording(bridge)) {
2852 astman_send_error(s, m, "Conference is already being recorded.");
2854 ao2_ref(bridge, -1);
2858 if (!ast_strlen_zero(recordfile)) {
2859 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2862 if (start_conf_record_thread(bridge)) {
2863 astman_send_error(s, m, "Internal error starting conference recording.");
2865 ao2_ref(bridge, -1);
2870 ao2_ref(bridge, -1);
2871 astman_send_ack(s, m, "Conference Recording Started.");
2874 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2876 const char *conference = astman_get_header(m, "Conference");
2877 struct conference_bridge *bridge = NULL;
2878 struct conference_bridge tmp;
2880 if (ast_strlen_zero(conference)) {
2881 astman_send_error(s, m, "No Conference name provided.");
2884 if (!ao2_container_count(conference_bridges)) {
2885 astman_send_error(s, m, "No active conferences.");
2889 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2890 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2892 astman_send_error(s, m, "No Conference by that name found.");
2897 if (conf_stop_record(bridge)) {
2899 astman_send_error(s, m, "Internal error while stopping recording.");
2900 ao2_ref(bridge, -1);
2905 ao2_ref(bridge, -1);
2906 astman_send_ack(s, m, "Conference Recording Stopped.");
2910 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2912 const char *conference = astman_get_header(m, "Conference");
2913 const char *channel = astman_get_header(m, "Channel");
2914 struct conference_bridge_user *participant = NULL;
2915 struct conference_bridge *bridge = NULL;
2916 struct conference_bridge tmp;
2918 if (ast_strlen_zero(conference)) {
2919 astman_send_error(s, m, "No Conference name provided.");
2922 if (ast_strlen_zero(channel)) {
2923 astman_send_error(s, m, "No channel name provided.");
2926 if (!ao2_container_count(conference_bridges)) {
2927 astman_send_error(s, m, "No active conferences.");
2931 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2932 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2934 astman_send_error(s, m, "No Conference by that name found.");
2938 /* find channel and set as video src. */
2940 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2941 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
2942 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2947 ao2_ref(bridge, -1);
2949 /* do not access participant after bridge unlock. We are just
2950 * using this check to see if it was found or not */
2952 astman_send_error(s, m, "No channel by that name found in conference.");
2955 astman_send_ack(s, m, "Conference single video source set.");
2959 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2962 struct conference_bridge *bridge = NULL;
2963 struct conference_bridge_user *participant = NULL;
2964 struct conference_bridge tmp;
2966 AST_DECLARE_APP_ARGS(args,
2968 AST_APP_ARG(confno);
2971 /* parse all the required arguments and make sure they exist. */
2972 if (ast_strlen_zero(data)) {
2975 parse = ast_strdupa(data);
2976 AST_STANDARD_APP_ARGS(args, parse);
2977 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
2980 if (!ao2_container_count(conference_bridges)) {
2981 snprintf(buf, len, "0");
2984 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
2985 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2987 snprintf(buf, len, "0");
2991 /* get the correct count for the type requested */
2993 if (!strncasecmp(args.type, "parties", 7)) {
2994 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2997 } else if (!strncasecmp(args.type, "admins", 6)) {
2998 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2999 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
3003 } else if (!strncasecmp(args.type, "marked", 6)) {
3004 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
3005 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
3009 } else if (!strncasecmp(args.type, "locked", 6)) {
3010 count = bridge->locked;
3012 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
3013 "parties, admins, marked, or locked.\n", args.type);
3015 snprintf(buf, len, "%d", count);
3017 ao2_ref(bridge, -1);
3021 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3023 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
3024 conference_bridge->activeusers++;
3027 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3029 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
3030 conference_bridge->activeusers++;
3031 conference_bridge->markedusers++;
3034 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3036 AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
3037 conference_bridge->waitingusers++;
3040 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3042 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
3043 conference_bridge->activeusers--;
3046 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3048 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
3049 conference_bridge->activeusers--;
3050 conference_bridge->markedusers--;
3053 void conf_mute_only_active(struct conference_bridge *conference_bridge)
3055 struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
3057 /* Turn on MOH/mute if the single participant is set up for it */
3058 if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
3059 only_participant->features.mute = 1;
3060 conf_moh_start(only_participant);
3064 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3066 AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
3067 conference_bridge->waitingusers--;
3070 /*! \brief Called when module is being unloaded */
3071 static int unload_module(void)
3073 int res = ast_unregister_application(app);
3075 ast_custom_function_unregister(&confbridge_function);
3076 ast_custom_function_unregister(&confbridge_info_function);
3078 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
3080 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
3081 ao2_ref(conference_bridges, -1);
3083 conf_destroy_config();
3085 ast_channel_unregister(&record_tech);
3086 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
3088 res |= ast_manager_unregister("ConfbridgeList");
3089 res |= ast_manager_unregister("ConfbridgeListRooms");
3090 res |= ast_manager_unregister("ConfbridgeMute");
3091 res |= ast_manager_unregister("ConfbridgeUnmute");
3092 res |= ast_manager_unregister("ConfbridgeKick");
3093 res |= ast_manager_unregister("ConfbridgeUnlock");
3094 res |= ast_manager_unregister("ConfbridgeLock");
3095 res |= ast_manager_unregister("ConfbridgeStartRecord");
3096 res |= ast_manager_unregister("ConfbridgeStopRecord");
3097 res |= ast_manager_unregister("ConfbridgeSetSingleVideoSrc");
3103 * \brief Load the module
3105 * Module loading including tests for configuration or dependencies.
3106 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
3107 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
3108 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
3109 * configuration file or other non-critical problem return
3110 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
3112 static int load_module(void)
3116 if (conf_load_config(0)) {
3117 ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n");
3118 return AST_MODULE_LOAD_DECLINE;
3120 if ((ast_custom_function_register(&confbridge_function))) {
3121 return AST_MODULE_LOAD_FAILURE;
3123 if ((ast_custom_function_register(&confbridge_info_function))) {
3124 return AST_MODULE_LOAD_FAILURE;
3126 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
3127 return AST_MODULE_LOAD_FAILURE;
3129 ast_format_cap_add_all(record_tech.capabilities);
3130 if (ast_channel_register(&record_tech)) {
3131 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
3132 return AST_MODULE_LOAD_FAILURE;
3134 /* Create a container to hold the conference bridges */
3135 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
3136 return AST_MODULE_LOAD_FAILURE;
3138 if (ast_register_application_xml(app, confbridge_exec)) {
3139 ao2_ref(conference_bridges, -1);
3140 return AST_MODULE_LOAD_FAILURE;
3143 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
3144 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
3145 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
3146 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);