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, int waiting)
2229 char flag_str[5 + 1];/* Max flags + terminator */
2232 /* Build flags column string. */
2233 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
2234 flag_str[pos++] = 'A';
2236 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
2237 flag_str[pos++] = 'M';
2239 if (ast_test_flag(&participant->u_profile, USER_OPT_WAITMARKED)) {
2240 flag_str[pos++] = 'W';
2242 if (ast_test_flag(&participant->u_profile, USER_OPT_ENDMARKED)) {
2243 flag_str[pos++] = 'E';
2246 flag_str[pos++] = 'w';
2248 flag_str[pos] = '\0';
2250 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
2251 ast_cli(a->fd, "%-5s ", flag_str);
2252 ast_cli(a->fd, "%-17s", participant->u_profile.name);
2253 ast_cli(a->fd, "%-17s", participant->b_profile.name);
2254 ast_cli(a->fd, "%-17s", participant->menu_name);
2255 ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
2256 ast_cli(a->fd, "\n");
2259 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2261 struct conference_bridge *bridge;
2265 e->command = "confbridge list";
2267 "Usage: confbridge list [<name>]\n"
2268 " Lists all currently active conference bridges or a specific conference bridge.\n"
2270 " When a conference bridge name is provided, flags may be shown for users. Below\n"
2271 " are the flags and what they represent.\n"
2274 " A - The user is an admin\n"
2275 " M - The user is a marked user\n"
2276 " W - The user must wait for a marked user to join\n"
2277 " E - The user will be kicked after the last marked user leaves the conference\n"
2278 " w - The user is waiting for a marked user to join\n";
2282 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2288 struct ao2_iterator iter;
2290 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
2291 ast_cli(a->fd, "================================ ====== ====== ========\n");
2292 iter = ao2_iterator_init(conference_bridges, 0);
2293 while ((bridge = ao2_iterator_next(&iter))) {
2294 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers + bridge->waitingusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
2295 ao2_ref(bridge, -1);
2297 ao2_iterator_destroy(&iter);
2302 struct conference_bridge_user *participant;
2303 struct conference_bridge tmp;
2305 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2306 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2308 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2311 ast_cli(a->fd, "Channel Flags User Profile Bridge Profile Menu CallerID\n");
2312 ast_cli(a->fd, "============================= ===== ================ ================ ================ ================\n");
2314 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2315 handle_cli_confbridge_list_item(a, participant, 0);
2317 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
2318 handle_cli_confbridge_list_item(a, participant, 1);
2321 ao2_ref(bridge, -1);
2325 return CLI_SHOWUSAGE;
2329 * \brief finds a conference by name and locks/unlocks.
2332 * \retval -1 conference not found
2334 static int generic_lock_unlock_helper(int lock, const char *conference)
2336 struct conference_bridge *bridge = NULL;
2337 struct conference_bridge tmp;
2340 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2341 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2346 bridge->locked = lock;
2347 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
2349 ao2_ref(bridge, -1);
2355 * \brief finds a conference user by channel name and mutes/unmutes them.
2358 * \retval -1 conference not found
2359 * \retval -2 user not found
2361 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
2363 struct conference_bridge *bridge = NULL;
2364 struct conference_bridge tmp;
2365 struct conference_bridge_user *participant = NULL;
2367 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2368 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2373 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2374 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
2379 participant->features.mute = mute;
2380 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));
2385 ao2_ref(bridge, -1);
2390 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2392 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2395 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2397 } else if (res == -2) {
2398 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2401 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2405 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2409 e->command = "confbridge mute";
2411 "Usage: confbridge mute <conference> <channel>\n";
2415 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2420 return CLI_SHOWUSAGE;
2423 cli_mute_unmute_helper(1, a);
2428 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2432 e->command = "confbridge unmute";
2434 "Usage: confbridge unmute <conference> <channel>\n";
2438 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2443 return CLI_SHOWUSAGE;
2446 cli_mute_unmute_helper(0, a);
2451 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2455 e->command = "confbridge lock";
2457 "Usage: confbridge lock <conference>\n";
2461 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2466 return CLI_SHOWUSAGE;
2468 if (generic_lock_unlock_helper(1, a->argv[2])) {
2469 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2471 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2476 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2480 e->command = "confbridge unlock";
2482 "Usage: confbridge unlock <conference>\n";
2486 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2491 return CLI_SHOWUSAGE;
2493 if (generic_lock_unlock_helper(0, a->argv[2])) {
2494 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2496 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2501 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2503 const char *rec_file = NULL;
2504 struct conference_bridge *bridge = NULL;
2505 struct conference_bridge tmp;
2509 e->command = "confbridge record start";
2511 "Usage: confbridge record start <conference> <file>\n"
2512 " <file> is optional, Otherwise the bridge profile\n"
2513 " record file will be used. If the bridge profile\n"
2514 " has no record file specified, a file will automatically\n"
2515 " be generated in the monitor directory\n";
2519 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2524 return CLI_SHOWUSAGE;
2527 rec_file = a->argv[4];
2530 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2531 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2533 ast_cli(a->fd, "Conference not found.\n");
2537 if (conf_is_recording(bridge)) {
2538 ast_cli(a->fd, "Conference is already being recorded.\n");
2540 ao2_ref(bridge, -1);
2543 if (!ast_strlen_zero(rec_file)) {
2544 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2547 if (start_conf_record_thread(bridge)) {
2548 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2550 ao2_ref(bridge, -1);
2555 ast_cli(a->fd, "Recording started\n");
2556 ao2_ref(bridge, -1);
2560 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2562 struct conference_bridge *bridge = NULL;
2563 struct conference_bridge tmp;
2568 e->command = "confbridge record stop";
2570 "Usage: confbridge record stop <conference>\n";
2574 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2579 return CLI_SHOWUSAGE;
2582 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2583 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2585 ast_cli(a->fd, "Conference not found.\n");
2589 ret = conf_stop_record(bridge);
2591 ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
2592 ao2_ref(bridge, -1);
2596 static struct ast_cli_entry cli_confbridge[] = {
2597 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2598 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2599 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2600 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
2601 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2602 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2603 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2604 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2606 static struct ast_custom_function confbridge_function = {
2607 .name = "CONFBRIDGE",
2608 .write = func_confbridge_helper,
2611 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2612 static struct ast_custom_function confbridge_info_function = {
2613 .name = "CONFBRIDGE_INFO",
2614 .read = func_confbridge_info,
2617 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct conference_bridge *bridge, struct conference_bridge_user *participant, int waiting)
2620 "Event: ConfbridgeList\r\n"
2622 "Conference: %s\r\n"
2623 "CallerIDNum: %s\r\n"
2624 "CallerIDName: %s\r\n"
2627 "MarkedUser: %s\r\n"
2628 "WaitMarked: %s\r\n"
2634 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
2635 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
2636 ast_channel_name(participant->chan),
2637 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2638 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No",
2639 ast_test_flag(&participant->u_profile, USER_OPT_WAITMARKED) ? "Yes" : "No",
2640 ast_test_flag(&participant->u_profile, USER_OPT_ENDMARKED) ? "Yes" : "No",
2641 waiting ? "Yes" : "No");
2644 static int action_confbridgelist(struct mansession *s, const struct message *m)
2646 const char *actionid = astman_get_header(m, "ActionID");
2647 const char *conference = astman_get_header(m, "Conference");
2648 struct conference_bridge_user *participant;
2649 struct conference_bridge *bridge;
2650 struct conference_bridge tmp;
2655 if (!ast_strlen_zero(actionid)) {
2656 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2658 if (ast_strlen_zero(conference)) {
2659 astman_send_error(s, m, "No Conference name provided.");
2662 if (!ao2_container_count(conference_bridges)) {
2663 astman_send_error(s, m, "No active conferences.");
2666 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2667 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2669 astman_send_error(s, m, "No Conference by that name found.");
2673 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2676 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2678 action_confbridgelist_item(s, id_text, bridge, participant, 0);
2680 AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
2682 action_confbridgelist_item(s, id_text, bridge, participant, 1);
2685 ao2_ref(bridge, -1);
2688 "Event: ConfbridgeListComplete\r\n"
2689 "EventList: Complete\r\n"
2692 "\r\n", total, id_text);
2697 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2699 const char *actionid = astman_get_header(m, "ActionID");
2700 struct conference_bridge *bridge = NULL;
2701 struct ao2_iterator iter;
2702 char id_text[512] = "";
2705 if (!ast_strlen_zero(actionid)) {
2706 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2709 if (!ao2_container_count(conference_bridges)) {
2710 astman_send_error(s, m, "No active conferences.");
2714 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2716 /* Traverse the conference list */
2717 iter = ao2_iterator_init(conference_bridges, 0);
2718 while ((bridge = ao2_iterator_next(&iter))) {
2723 "Event: ConfbridgeListRooms\r\n"
2725 "Conference: %s\r\n"
2732 bridge->activeusers + bridge->waitingusers,
2733 bridge->markedusers,
2734 bridge->locked ? "Yes" : "No");
2737 ao2_ref(bridge, -1);
2739 ao2_iterator_destroy(&iter);
2741 /* Send final confirmation */
2743 "Event: ConfbridgeListRoomsComplete\r\n"
2744 "EventList: Complete\r\n"
2747 "\r\n", totalitems, id_text);
2751 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2753 const char *conference = astman_get_header(m, "Conference");
2754 const char *channel = astman_get_header(m, "Channel");
2757 if (ast_strlen_zero(conference)) {
2758 astman_send_error(s, m, "No Conference name provided.");
2761 if (ast_strlen_zero(channel)) {
2762 astman_send_error(s, m, "No channel name provided.");
2765 if (!ao2_container_count(conference_bridges)) {
2766 astman_send_error(s, m, "No active conferences.");
2770 res = generic_mute_unmute_helper(mute, conference, channel);
2773 astman_send_error(s, m, "No Conference by that name found.");
2775 } else if (res == -2) {
2776 astman_send_error(s, m, "No Channel by that name found in Conference.");
2780 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2784 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2786 return action_mute_unmute_helper(s, m, 0);
2788 static int action_confbridgemute(struct mansession *s, const struct message *m)
2790 return action_mute_unmute_helper(s, m, 1);
2793 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2795 const char *conference = astman_get_header(m, "Conference");
2798 if (ast_strlen_zero(conference)) {
2799 astman_send_error(s, m, "No Conference name provided.");
2802 if (!ao2_container_count(conference_bridges)) {
2803 astman_send_error(s, m, "No active conferences.");
2806 if ((res = generic_lock_unlock_helper(lock, conference))) {
2807 astman_send_error(s, m, "No Conference by that name found.");
2810 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2813 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2815 return action_lock_unlock_helper(s, m, 0);
2817 static int action_confbridgelock(struct mansession *s, const struct message *m)
2819 return action_lock_unlock_helper(s, m, 1);
2822 static int action_confbridgekick(struct mansession *s, const struct message *m)
2824 const char *conference = astman_get_header(m, "Conference");
2825 const char *channel = astman_get_header(m, "Channel");
2826 struct conference_bridge_user *participant = NULL;
2827 struct conference_bridge *bridge = NULL;
2828 struct conference_bridge tmp;
2831 if (ast_strlen_zero(conference)) {
2832 astman_send_error(s, m, "No Conference name provided.");
2835 if (!ao2_container_count(conference_bridges)) {
2836 astman_send_error(s, m, "No active conferences.");
2839 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2840 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2842 astman_send_error(s, m, "No Conference by that name found.");
2847 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2848 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
2849 participant->kicked = 1;
2850 ast_bridge_remove(bridge->bridge, participant->chan);
2856 ao2_ref(bridge, -1);
2859 astman_send_ack(s, m, "User kicked");
2861 astman_send_error(s, m, "No Channel by that name found in Conference.");
2866 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2868 const char *conference = astman_get_header(m, "Conference");
2869 const char *recordfile = astman_get_header(m, "RecordFile");
2870 struct conference_bridge *bridge = NULL;
2871 struct conference_bridge tmp;
2873 if (ast_strlen_zero(conference)) {
2874 astman_send_error(s, m, "No Conference name provided.");
2877 if (!ao2_container_count(conference_bridges)) {
2878 astman_send_error(s, m, "No active conferences.");
2882 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2883 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2885 astman_send_error(s, m, "No Conference by that name found.");
2890 if (conf_is_recording(bridge)) {
2891 astman_send_error(s, m, "Conference is already being recorded.");
2893 ao2_ref(bridge, -1);
2897 if (!ast_strlen_zero(recordfile)) {
2898 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2901 if (start_conf_record_thread(bridge)) {
2902 astman_send_error(s, m, "Internal error starting conference recording.");
2904 ao2_ref(bridge, -1);
2909 ao2_ref(bridge, -1);
2910 astman_send_ack(s, m, "Conference Recording Started.");
2913 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2915 const char *conference = astman_get_header(m, "Conference");
2916 struct conference_bridge *bridge = NULL;
2917 struct conference_bridge tmp;
2919 if (ast_strlen_zero(conference)) {
2920 astman_send_error(s, m, "No Conference name provided.");
2923 if (!ao2_container_count(conference_bridges)) {
2924 astman_send_error(s, m, "No active conferences.");
2928 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2929 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2931 astman_send_error(s, m, "No Conference by that name found.");
2936 if (conf_stop_record(bridge)) {
2938 astman_send_error(s, m, "Internal error while stopping recording.");
2939 ao2_ref(bridge, -1);
2944 ao2_ref(bridge, -1);
2945 astman_send_ack(s, m, "Conference Recording Stopped.");
2949 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2951 const char *conference = astman_get_header(m, "Conference");
2952 const char *channel = astman_get_header(m, "Channel");
2953 struct conference_bridge_user *participant = NULL;
2954 struct conference_bridge *bridge = NULL;
2955 struct conference_bridge tmp;
2957 if (ast_strlen_zero(conference)) {
2958 astman_send_error(s, m, "No Conference name provided.");
2961 if (ast_strlen_zero(channel)) {
2962 astman_send_error(s, m, "No channel name provided.");
2965 if (!ao2_container_count(conference_bridges)) {
2966 astman_send_error(s, m, "No active conferences.");
2970 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2971 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2973 astman_send_error(s, m, "No Conference by that name found.");
2977 /* find channel and set as video src. */
2979 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
2980 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
2981 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2986 ao2_ref(bridge, -1);
2988 /* do not access participant after bridge unlock. We are just
2989 * using this check to see if it was found or not */
2991 astman_send_error(s, m, "No channel by that name found in conference.");
2994 astman_send_ack(s, m, "Conference single video source set.");
2998 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
3001 struct conference_bridge *bridge = NULL;
3002 struct conference_bridge_user *participant = NULL;
3003 struct conference_bridge tmp;
3005 AST_DECLARE_APP_ARGS(args,
3007 AST_APP_ARG(confno);
3010 /* parse all the required arguments and make sure they exist. */
3011 if (ast_strlen_zero(data)) {
3014 parse = ast_strdupa(data);
3015 AST_STANDARD_APP_ARGS(args, parse);
3016 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
3019 if (!ao2_container_count(conference_bridges)) {
3020 snprintf(buf, len, "0");
3023 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
3024 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
3026 snprintf(buf, len, "0");
3030 /* get the correct count for the type requested */
3032 if (!strncasecmp(args.type, "parties", 7)) {
3033 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
3036 } else if (!strncasecmp(args.type, "admins", 6)) {
3037 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
3038 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
3042 } else if (!strncasecmp(args.type, "marked", 6)) {
3043 AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
3044 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
3048 } else if (!strncasecmp(args.type, "locked", 6)) {
3049 count = bridge->locked;
3051 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
3052 "parties, admins, marked, or locked.\n", args.type);
3054 snprintf(buf, len, "%d", count);
3056 ao2_ref(bridge, -1);
3060 void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3062 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
3063 conference_bridge->activeusers++;
3066 void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3068 AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list);
3069 conference_bridge->activeusers++;
3070 conference_bridge->markedusers++;
3073 void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3075 AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list);
3076 conference_bridge->waitingusers++;
3079 void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3081 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
3082 conference_bridge->activeusers--;
3085 void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3087 AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list);
3088 conference_bridge->activeusers--;
3089 conference_bridge->markedusers--;
3092 void conf_mute_only_active(struct conference_bridge *conference_bridge)
3094 struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list);
3096 /* Turn on MOH/mute if the single participant is set up for it */
3097 if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) {
3098 only_participant->features.mute = 1;
3099 conf_moh_start(only_participant);
3103 void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu)
3105 AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list);
3106 conference_bridge->waitingusers--;
3109 /*! \brief Called when module is being unloaded */
3110 static int unload_module(void)
3112 int res = ast_unregister_application(app);
3114 ast_custom_function_unregister(&confbridge_function);
3115 ast_custom_function_unregister(&confbridge_info_function);
3117 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
3119 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
3120 ao2_ref(conference_bridges, -1);
3122 conf_destroy_config();
3124 ast_channel_unregister(&record_tech);
3125 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
3127 res |= ast_manager_unregister("ConfbridgeList");
3128 res |= ast_manager_unregister("ConfbridgeListRooms");
3129 res |= ast_manager_unregister("ConfbridgeMute");
3130 res |= ast_manager_unregister("ConfbridgeUnmute");
3131 res |= ast_manager_unregister("ConfbridgeKick");
3132 res |= ast_manager_unregister("ConfbridgeUnlock");
3133 res |= ast_manager_unregister("ConfbridgeLock");
3134 res |= ast_manager_unregister("ConfbridgeStartRecord");
3135 res |= ast_manager_unregister("ConfbridgeStopRecord");
3136 res |= ast_manager_unregister("ConfbridgeSetSingleVideoSrc");