diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala index 743a4894c4acec763614b2f9d50ca560e59ba24b..c393e3d1c5f8eb881a53fcba4b64f89d4a9e1b3e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala @@ -19,6 +19,8 @@ trait SystemConfiguration extends RedisConfiguration { lazy val expireLastUserLeft = Try(config.getInt("expire.lastUserLeft")).getOrElse(60) // 1 minute lazy val expireNeverJoined = Try(config.getInt("expire.neverJoined")).getOrElse(5 * 60) // 5 minutes + lazy val maxRegUserToJoinTime = Try(config.getInt("expire.maxRegUserToJoin")).getOrElse(5 * 60) // 5 minutes + lazy val analyticsChannel = Try(config.getString("eventBus.analyticsChannel")).getOrElse("analytics-channel") lazy val meetingManagerChannel = Try(config.getString("eventBus.meetingManagerChannel")).getOrElse("MeetingManagerChannel") lazy val outMessageChannel = Try(config.getString("eventBus.outMessageChannel")).getOrElse("OutgoingMessageChannel") @@ -40,10 +42,9 @@ trait SystemConfiguration extends RedisConfiguration { lazy val applyPermissionCheck = Try(config.getBoolean("apps.checkPermissions")).getOrElse(false) lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings") - lazy val recordingChapterBreakLenghtInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(180) + lazy val recordingChapterBreakLengthInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(0) lazy val endMeetingWhenNoMoreAuthedUsers = Try(config.getBoolean("apps.endMeetingWhenNoMoreAuthedUsers")).getOrElse(false) lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2) lazy val multiUserWhiteboardDefault = Try(config.getBoolean("whiteboard.multiUserDefault")).getOrElse(false) } - diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala index e4e4da045a51cb46250b7601fcf01bf1b8ce9154..678dfe94452b7bd92f0da5e4a58549896195ac5b 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/CreateBreakoutRoomsCmdMsgHdlr.scala @@ -55,6 +55,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait { liveMeeting.props.meetingProp.intId, breakout.sequence, breakout.freeJoin, + liveMeeting.props.voiceProp.dialNumber, breakout.voiceConf, msg.body.durationInMinutes, liveMeeting.props.password.moderatorPass, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala index 5cce9c4908f83e8d1488a5510b46cb045d65fc6a..12dafc857e63664e905329e5a23f81f650adcd3f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala @@ -7,7 +7,18 @@ object RegisteredUsers { def create(userId: String, extId: String, name: String, roles: String, token: String, avatar: String, guest: Boolean, authenticated: Boolean, guestStatus: String): RegisteredUser = { - new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, guestStatus) + new RegisteredUser(userId, + extId, + name, + roles, + token, + avatar, + guest, + authenticated, + guestStatus, + System.currentTimeMillis(), + false, + false) } def findWithToken(token: String, users: RegisteredUsers): Option[RegisteredUser] = { @@ -18,6 +29,10 @@ object RegisteredUsers { users.toVector.find(ru => id == ru.id) } + def findUsersNotJoined(users: RegisteredUsers):Vector[RegisteredUser] = { + users.toVector.filter(u => u.joined == false && u.markAsJoinTimedOut == false) + } + def getRegisteredUserWithToken(token: String, userId: String, regUsers: RegisteredUsers): Option[RegisteredUser] = { def isSameUserId(ru: RegisteredUser, userId: String): Option[RegisteredUser] = { if (userId.startsWith(ru.id)) { @@ -33,14 +48,6 @@ object RegisteredUsers { } yield user } - def updateRegUser(uvo: UserVO, users: RegisteredUsers) { - for { - ru <- RegisteredUsers.findWithUserId(uvo.id, users) - regUser = new RegisteredUser(uvo.id, uvo.externalId, uvo.name, uvo.role, ru.authToken, - uvo.avatarURL, uvo.guest, uvo.authed, uvo.guestStatus) - } yield users.save(regUser) - } - def add(users: RegisteredUsers, user: RegisteredUser): Vector[RegisteredUser] = { users.save(user) } @@ -63,6 +70,17 @@ object RegisteredUsers { u } + def updateUserJoin(users: RegisteredUsers, user: RegisteredUser): RegisteredUser = { + val u = user.copy(joined = true) + users.save(u) + u + } + + def markAsUserFailedToJoin(users: RegisteredUsers, user: RegisteredUser): RegisteredUser = { + val u = user.copy(markAsJoinTimedOut = true) + users.save(u) + u + } } class RegisteredUsers { @@ -84,7 +102,16 @@ class RegisteredUsers { } } -case class RegisteredUser(id: String, externId: String, name: String, role: String, - authToken: String, avatarURL: String, guest: Boolean, - authed: Boolean, guestStatus: String) +case class RegisteredUser(id: String, + externId: String, + name: String, + role: String, + authToken: String, + avatarURL: String, + guest: Boolean, + authed: Boolean, + guestStatus: String, + registeredOn: Long, + joined: Boolean, + markAsJoinTimedOut: Boolean) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index fd9294e20602e3211a071698ed6ea476f518e386..eb1cd57adaac565b7a9865c976d65c8caf3d78b9 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -26,12 +26,22 @@ trait HandlerHelpers extends SystemConfiguration { outGW.send(event) } + def trackUserJoin(outGW: OutMsgRouter, + liveMeeting: LiveMeeting, + regUser: RegisteredUser): Unit = { + if (!regUser.joined) { + RegisteredUsers.updateUserJoin(liveMeeting.registeredUsers, regUser) + } + } + def userJoinMeeting(outGW: OutMsgRouter, authToken: String, clientType: String, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = { val nu = for { regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers) } yield { + trackUserJoin(outGW, liveMeeting, regUser) + UserState( intId = regUser.id, extId = regUser.externId, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 5cf811b39fed2e549407996a8488b26207171ff2..84741d152da8040e0146fc778461c5f4e12f3ac8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -506,7 +506,7 @@ class MeetingActor( setRecordingChapterBreak() processUserInactivityAudit() - + flagRegisteredUsersWhoHasNotJoined() checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting) } @@ -517,7 +517,8 @@ class MeetingActor( val elapsedInMs = now - lastRecBreakSentOn val elapsedInMin = TimeUtil.millisToMinutes(elapsedInMs) - if (elapsedInMin > recordingChapterBreakLenghtInMinutes) { + if (recordingChapterBreakLengthInMinutes > 0 && + elapsedInMin > recordingChapterBreakLengthInMinutes) { lastRecBreakSentOn = now val event = MsgBuilder.buildRecordingChapterBreakSysMsg(props.meetingProp.intId, TimeUtil.timeNowInMs()) outGW.send(event) @@ -657,4 +658,19 @@ class MeetingActor( } } } + + def flagRegisteredUsersWhoHasNotJoined(): Unit = { + val users = RegisteredUsers.findUsersNotJoined(liveMeeting.registeredUsers) + users foreach{ u => + val now = System.currentTimeMillis() + if (now - u.registeredOn > TimeUtil.secondsToMillis(maxRegUserToJoinTime)) { + RegisteredUsers.markAsUserFailedToJoin(liveMeeting.registeredUsers, u) + val event = MsgBuilder.buildRegisteredUserJoinTimeoutMsg( + liveMeeting.props.meetingProp.intId, + u.id, + u.name) + outGW.send(event) + } + } + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala index d24d5dfa101b848ba3d669f8c59963bdd71f4919..3e897cf5d96ebe9d9cd1dbf402bc4253952737d0 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala @@ -20,6 +20,10 @@ object TimeUtil { TimeUnit.MINUTES.toSeconds(minutes) } + def secondsToMillis(seconds: Long): Long = { + TimeUnit.SECONDS.toMillis(seconds) + } + def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime()) def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()) def timeNowInMs(): Long = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala index 7a4eec74e32c271c10b4bb4af2e6e050b86c123e..44c375a77289a2343e16a55379e9928bbcf6b66a 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala @@ -31,6 +31,7 @@ class AnalyticsActor extends Actor with ActorLogging { msg.core match { case m: RegisterUserReqMsg => logMessage(msg) + case m: RegisteredUserJoinTimeoutMsg => logMessage(msg) case m: UserRegisteredRespMsg => logMessage(msg) case m: DisconnectAllClientsSysMsg => logMessage(msg) case m: DisconnectClientSysMsg => logMessage(msg) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index 220314a53a929a27268d77a6c40eb684c51bee63..ac693a9b1c1dc71a4cad8998a4192d3ed8c53384 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -317,4 +317,13 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } + + def buildRegisteredUserJoinTimeoutMsg(meetingId: String, userId: String, name: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(RegisteredUserJoinTimeoutMsg.NAME, routing) + val header = BbbCoreHeaderWithMeetingId(RegisteredUserJoinTimeoutMsg.NAME, meetingId) + val body = RegisteredUserJoinTimeoutMsgBody(meetingId, userId, name) + val event = RegisteredUserJoinTimeoutMsg(header, body) + BbbCommonEnvCoreMsg(envelope, event) + } } diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala index 3e57891347a7b653e8fe4392aea6854d7065ddc1..2fcfc442176b2afbf5f40b7f3a95c84c06540371 100755 --- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala +++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala @@ -26,6 +26,7 @@ trait AppsTestFixtures { val userInactivityThresholdInMinutes = 10 val userActivitySignResponseDelayInMinutes = 5 val autoStartRecording = false + val keepEvents = false val allowStartStopRecording = false val webcamsOnlyForModerator = false; val moderatorPassword = "modpass" @@ -53,7 +54,7 @@ trait AppsTestFixtures { userInactivityInspectTimerInMinutes = userInactivityInspectTimerInMinutes, userInactivityThresholdInMinutes = userInactivityInspectTimerInMinutes, userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes) val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword) val recordProp = RecordProp(record = record, autoStartRecording = autoStartRecording, - allowStartStopRecording = allowStartStopRecording) + allowStartStopRecording = allowStartStopRecording, keepEvents = keepEvents ) val welcomeProp = WelcomeProp(welcomeMsgTemplate = welcomeMsgTemplate, welcomeMsg = welcomeMsg, modOnlyMessage = modOnlyMessage) val voiceProp = VoiceProp(telVoice = voiceConfId, voiceConf = voiceConfId, dialNumber = dialNumber, muteOnStart = muteOnStart) diff --git a/akka-bbb-apps/src/universal/conf/application.conf b/akka-bbb-apps/src/universal/conf/application.conf index 591f22a88f5ef2613020407b8dc6f493bb6cf046..22387092ca02e8dec6eb7e5713500f277b8d670b 100755 --- a/akka-bbb-apps/src/universal/conf/application.conf +++ b/akka-bbb-apps/src/universal/conf/application.conf @@ -39,6 +39,7 @@ expire { # time in seconds lastUserLeft = 60 neverJoined = 300 + maxRegUserToJoin = 300 } services { @@ -84,7 +85,8 @@ voiceConf { } recording { - chapterBreakLengthInMinutes = 180 + # set zero to disable chapter break + chapterBreakLengthInMinutes = 0 } whiteboard { diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala index 0048b8fe85a16ba3a9bdf722cb52fe1c48a274d1..deb7848ece378e2dc3e3afd8fd0d2fb4506bdb83 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/BreakoutMsgs.scala @@ -39,9 +39,9 @@ package org.bigbluebutton.common2.msgs case class CreateBreakoutRoomSysCmdMsg(header: BbbCoreBaseHeader, body: CreateBreakoutRoomSysCmdMsgBody) extends BbbCoreMsg case class CreateBreakoutRoomSysCmdMsgBody(meetingId: String, room: BreakoutRoomDetail) - case class BreakoutRoomDetail(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer, freeJoin:Boolean, - voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String, - sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean) + case class BreakoutRoomDetail(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer, freeJoin: Boolean, + dialNumber: String, voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, + viewerPassword: String, sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean) /** * Sent by client to request to create breakout rooms. diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMgs.scala index dea63077aa094b6978c12454b3372d0c602a98c8..b3295c269cb9660312f2b61c520521bbb7d51a45 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/UsersMgs.scala @@ -18,6 +18,11 @@ case class UserRegisteredRespMsg(header: BbbCoreHeaderWithMeetingId, body: UserRegisteredRespMsgBody) extends BbbCoreMsg case class UserRegisteredRespMsgBody(meetingId: String, userId: String, name: String, role: String) +object RegisteredUserJoinTimeoutMsg { val NAME = "RegisteredUserJoinTimeoutMsg" } +case class RegisteredUserJoinTimeoutMsg(header: BbbCoreHeaderWithMeetingId, + body: RegisteredUserJoinTimeoutMsgBody) extends BbbCoreMsg +case class RegisteredUserJoinTimeoutMsgBody(meetingId: String, userId: String, name: String) + object ValidateAuthTokenReqMsg { val NAME = "ValidateAuthTokenReqMsg" diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java index 95803cc58be22488493aa3891a8e24daafa61d9a..2d3f5cc3a4916c63b7b751cdc8858f1be58a0540 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java @@ -474,18 +474,19 @@ public class MeetingService implements MessageListener { if (parentMeeting != null) { Map<String, String> params = new HashMap<>(); - params.put("name", message.name); - params.put("meetingID", message.meetingId); - params.put("parentMeetingID", message.parentMeetingId); - params.put("isBreakout", "true"); - params.put("sequence", message.sequence.toString()); - params.put("freeJoin", message.freeJoin.toString()); - params.put("attendeePW", message.viewerPassword); - params.put("moderatorPW", message.moderatorPassword); - params.put("voiceBridge", message.voiceConfId); - params.put("duration", message.durationInMinutes.toString()); - params.put("record", message.record.toString()); - params.put("welcome", getMeeting(message.parentMeetingId).getWelcomeMessageTemplate()); + params.put(ApiParams.NAME, message.name); + params.put(ApiParams.MEETING_ID, message.meetingId); + params.put(ApiParams.PARENT_MEETING_ID, message.parentMeetingId); + params.put(ApiParams.IS_BREAKOUT, "true"); + params.put(ApiParams.SEQUENCE, message.sequence.toString()); + params.put(ApiParams.FREE_JOIN, message.freeJoin.toString()); + params.put(ApiParams.ATTENDEE_PW, message.viewerPassword); + params.put(ApiParams.MODERATOR_PW, message.moderatorPassword); + params.put(ApiParams.DIAL_NUMBER, message.dialNumber); + params.put(ApiParams.VOICE_BRIDGE, message.voiceConfId); + params.put(ApiParams.DURATION, message.durationInMinutes.toString()); + params.put(ApiParams.RECORD, message.record.toString()); + params.put(ApiParams.WELCOME, getMeeting(message.parentMeetingId).getWelcomeMessageTemplate()); Map<String, String> parentMeetingMetadata = parentMeeting.getMetadata(); diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java index 33915458bf8ab0d93795db61ba1c131423babd2b..df8789d9ddebe2492a3f05608e1b926425b5570f 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/messaging/messages/CreateBreakoutRoom.java @@ -6,7 +6,9 @@ public class CreateBreakoutRoom implements IMessage { public final String parentMeetingId; // The main meeting internal id public final String name; // The name of the breakout room public final Integer sequence; // The sequence number of the breakout room - public final Boolean freeJoin; // Allow users to freely join the conference in the client + public final Boolean freeJoin; // Allow users to freely join the conference + // in the client + public final String dialNumber; public final String voiceConfId; // The voice conference id public final String viewerPassword; public final String moderatorPassword; @@ -15,16 +17,15 @@ public class CreateBreakoutRoom implements IMessage { public final Integer sourcePresentationSlide; public final Boolean record; - public CreateBreakoutRoom(String meetingId, String parentMeetingId, - String name, Integer sequence, Boolean freeJoin, String voiceConfId, - String viewerPassword, String moderatorPassword, Integer duration, - String sourcePresentationId, Integer sourcePresentationSlide, - Boolean record) { + public CreateBreakoutRoom(String meetingId, String parentMeetingId, String name, Integer sequence, Boolean freeJoin, + String dialNumber, String voiceConfId, String viewerPassword, String moderatorPassword, Integer duration, + String sourcePresentationId, Integer sourcePresentationSlide, Boolean record) { this.meetingId = meetingId; this.parentMeetingId = parentMeetingId; this.name = name; this.sequence = sequence; this.freeJoin = freeJoin; + this.dialNumber = dialNumber; this.voiceConfId = voiceConfId; this.viewerPassword = viewerPassword; this.moderatorPassword = moderatorPassword; diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java index 06248a30b260584c6d616765014d290c51d03bf0..ecb9dd3c717d9309704528900885f9ee90320b43 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/PdfToSwfSlidesGenerationService.java @@ -57,6 +57,7 @@ public class PdfToSwfSlidesGenerationService { private long MAX_CONVERSION_TIME = 5 * 60 * 1000L * 1000L * 1000L; private String BLANK_SLIDE; private int MAX_SWF_FILE_SIZE; + private boolean swfSlidesRequired; private boolean svgImagesRequired; private boolean generatePngs; @@ -64,26 +65,30 @@ public class PdfToSwfSlidesGenerationService { executor = Executors.newFixedThreadPool(numConversionThreads); } - public void generateSlides(UploadedPresentation pres) { - determineNumberOfPages(pres); - if (pres.getNumberOfPages() > 0) { - convertPdfToSwf(pres); - createTextFiles(pres); - createThumbnails(pres); + public void generateSlides(UploadedPresentation pres) { + determineNumberOfPages(pres); + if (pres.getNumberOfPages() > 0) { + // Only create SWF files if the configuration requires it + if (swfSlidesRequired) { + convertPdfToSwf(pres); + createThumbnails(pres); + } - // only create SVG images if the configuration requires it - if (svgImagesRequired) { - createSvgImages(pres); - } + createTextFiles(pres); + + // only create SVG images if the configuration requires it + if (svgImagesRequired) { + createSvgImages(pres); + } - // only create PNG images if the configuration requires it - if (generatePngs) { - createPngImages(pres); - } + // only create PNG images if the configuration requires it + if (generatePngs) { + createPngImages(pres); + } - notifier.sendConversionCompletedMessage(pres); + notifier.sendConversionCompletedMessage(pres); + } } - } private boolean determineNumberOfPages(UploadedPresentation pres) { try { @@ -331,9 +336,13 @@ public class PdfToSwfSlidesGenerationService { this.generatePngs = generatePngs; } - public void setSvgImagesRequired(boolean svg) { - this.svgImagesRequired = svg; - } + public void setSwfSlidesRequired(boolean swf) { + this.swfSlidesRequired = swf; + } + + public void setSvgImagesRequired(boolean svg) { + this.svgImagesRequired = svg; + } public void setThumbnailCreator(ThumbnailCreator thumbnailCreator) { this.thumbnailCreator = thumbnailCreator; diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/MeetingsManagerActor.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/MeetingsManagerActor.scala index 9a8f00075b244b3d371ad2751bb9b28629f6d7ca..9f4f2cb59af2009936cc911d95e6ebc6e7761255 100755 --- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/MeetingsManagerActor.scala +++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/MeetingsManagerActor.scala @@ -9,8 +9,8 @@ import org.bigbluebutton.common2.msgs.{BbbCommonEnvCoreMsg, MeetingCreatedEvtMsg sealed trait ApiMsg case class CreateBreakoutRoomMsg(meetingId: String, parentMeetingId: String, - name: String, sequence: Integer, freeJoin: Boolean, voiceConfId: String, - viewerPassword: String, moderatorPassword: String, duration: Int, + name: String, sequence: Integer, freeJoin: Boolean, dialNumber: String, + voiceConfId: String, viewerPassword: String, moderatorPassword: String, duration: Int, sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean) extends ApiMsg diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala index a80ea64206f107d6d7ebe35eaa31e0d562e2d3bd..7a7e376c2e54752cb54cf471e3d4bbcef9dae322 100755 --- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala +++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/meeting/OldMeetingMsgHdlrActor.scala @@ -75,6 +75,7 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW) msg.body.room.name, msg.body.room.sequence, msg.body.room.freeJoin, + msg.body.room.dialNumber, msg.body.room.voiceConfId, msg.body.room.viewerPassword, msg.body.room.moderatorPassword, diff --git a/bigbluebutton-config/bin/apply-lib.sh b/bigbluebutton-config/bin/apply-lib.sh index 2073bdc900e1635ca9ab88cbd820585c101a8220..6b6c330aeaf7aa1e0c2d1f3ba6d62fd722f80f0d 100644 --- a/bigbluebutton-config/bin/apply-lib.sh +++ b/bigbluebutton-config/bin/apply-lib.sh @@ -89,10 +89,10 @@ notCalled() { #!/bin/bash # Pull in the helper functions for configuring BigBlueButton -source apply-lib.sh +source /etc/bigbluebutton/bbb-conf/apply-lib.sh # Put your custom configuration here -enableHTML5ClientLog +# enableHTML5ClientLog HERE chmod +x /etc/bigbluebutton/bbb-conf/apply-config.sh diff --git a/bigbluebutton-config/bin/bbb-conf b/bigbluebutton-config/bin/bbb-conf index 1e65db7f213445b0aac145e1965005240d115ec8..cf45462a88b32135ad52077c7f7b9b4e3fadda90 100755 --- a/bigbluebutton-config/bin/bbb-conf +++ b/bigbluebutton-config/bin/bbb-conf @@ -1,6 +1,6 @@ -#!/bin/bash +#!/bin/bash # -# BlueButton open source conferencing system - http://www.bigbluebutton.org/ +# BlueButton open source conferencing system - http://www.bigbluebutton.org/ # # Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). # @@ -17,20 +17,20 @@ # with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # # Author(s): -# Fred Dixon <ffdixon@bigbluebutton.org> -# Sebastian Schneider <seb.sschneider@gmail.com> -# Ghazi Triki <ghazi.nocturne@gmail.com> +# Fred Dixon <ffdixon@bigbluebutton.org> +# Sebastian Schneider <seb.sschneider@gmail.com> +# Ghazi Triki <ghazi.nocturne@gmail.com> # # Changelog: -# 2009-10-18 FFD Inital Version -# 2009-11-05 FFD Updated for 0.62 -# 2009-12-09 FFD Updated for 0.63 -# 2009-12-11 FFD Added ability to switch conference servers -# 2009-12-12 FFD Added cleaning and watching of log files -# 2010-01-05 FFD Added zipping of log files -# 2010-01-18 FFD Added resetting of environment back to using packages -# 2010-03-02 JRT Added trunk checkout options / fixed bbb-apps instructions -# 2010-04-02 FFD Updated for 0.64 +# 2009-10-18 FFD Initial Version +# 2009-11-05 FFD Updated for 0.62 +# 2009-12-09 FFD Updated for 0.63 +# 2009-12-11 FFD Added ability to switch conference servers +# 2009-12-12 FFD Added cleaning and watching of log files +# 2010-01-05 FFD Added zipping of log files +# 2010-01-18 FFD Added resetting of environment back to using packages +# 2010-03-02 JRT Added trunk checkout options / fixed bbb-apps instructions +# 2010-04-02 FFD Updated for 0.64 # 2010-06-21 SEB Cleaned up some code / Updated for 0.70 # 2010-06-25 SEB Added ability to change the security secret # 2010-06-30 SEB Added some extra error checking @@ -40,15 +40,15 @@ # 2010-11-06 FFD Added logic to ensure red5 shuts down # 2010-12-12 FFD Fixed bug #778 # 2010-12-12 FFD Added support for Intalio VM -# 2010-02-28 FFD Fixed #834 +# 2010-02-28 FFD Fixed #834 # 2011-06-26 FFD Updates for 0.8 -# 2012-01-14 FFD Tsting the development environment for 0.8 +# 2012-01-14 FFD Testing the development environment for 0.8 # 2012-02-22 FFD Updates to development environment # 2012-04-27 FFD Added sum of version numbers in --check # 2013-02-03 FFD Updated for changes to parameters for 0.81 in bigbluebutton-sip.properties # 2013-11-07 FFD Finished 0.81 -# 2014-01-13 FFD Working on updates for 0.9.0 -# 2014-03-10 GUG Enable Webrtc +# 2014-01-13 FFD Working on updates for 0.9.0 +# 2014-03-10 GUG Enable WebRTC # 2015-03-12 FFD Added start/stop of HTML5 server # 2016-01-13 FFD Updates for 1.0 # 2016-02-28 FFD Updates to support HTTPS configuration @@ -59,77 +59,76 @@ # 2018-11-22 MNE Dynamically detect if sudo is needed # 2018-12-09 GTR More logs cleanup # 2019-02-08 GTR Updates for 2.2 after extracting bbb-web to a standalone server application +# 2019-03-14 GTR Refactoring and cleanup for 2.2 #set -x #set -e PATH=$PATH:/sbin -if [ "$(id -u)" != "0" ]; then - if [ -x "$(which sudo)" ]; then - SUDO="$(which sudo)" - else - echo "bbb-conf must be ran as root!" && exit 1 - fi +if [[ "$(id -u)" != "0" ]]; then + if [[ -x "$(which sudo)" ]]; then + SUDO="$(which sudo)" + else + echo "bbb-conf must be ran as root!" && exit 1 + fi fi -if [ ! -f /etc/bigbluebutton/bigbluebutton-release ]; then - echo - echo "# BigBlueButton does not appear to be installed. Could not" - echo "# locate: /etc/bigbluebutton/bigbluebutton-release" - echo - exit 1 +if [[ ! -f /etc/bigbluebutton/bigbluebutton-release ]]; then + echo + echo "# BigBlueButton does not appear to be installed. Could not" + echo "# locate: /etc/bigbluebutton/bigbluebutton-release" + echo + exit 1 fi +# Load the content of the file as variables source /etc/bigbluebutton/bigbluebutton-release # # Figure out our environment (Debian vs. CentOS) # -if [ -f /etc/redhat-release ]; then - DISTRIB_ID=centos - SERVLET_LOGS=/usr/share/tomcat/logs - FREESWITCH=freeswitch - TOMCAT_SERVICE=tomcat - REDIS_SERVICE=redis.service +if [ -f /etc/centos-release ]; then + DISTRIB_ID=centos + TOMCAT_SERVICE=tomcat + TOMCAT_DIR=/var/lib/$TOMCAT_SERVICE + SERVLET_LOGS=/usr/share/tomcat/logs + REDIS_SERVICE=redis.service else - . /etc/lsb-release # Get value for DISTRIB_ID - SERVLET_LOGS=$TOMCAT_DIR/logs - FREESWITCH=freeswitch - TOMCAT_SERVICE=tomcat7 - REDIS_SERVICE=redis-server + . /etc/lsb-release # Get value for DISTRIB_ID + TOMCAT_SERVICE=tomcat7 + TOMCAT_DIR=/var/lib/$TOMCAT_SERVICE + SERVLET_LOGS=$TOMCAT_DIR/logs + REDIS_SERVICE=redis-server fi +# Common to Ubuntu and CentOS +FREESWITCH_VARS=/opt/freeswitch/etc/freeswitch/vars.xml +FREESWITCH_EXTERNAL=/opt/freeswitch/etc/freeswitch/sip_profiles/external.xml +FREESWITCH_PID=/opt/freeswitch/var/run/freeswitch/freeswitch.pid +FREESWITCH_EVENT_SOCKET=/opt/freeswitch/etc/freeswitch/autoload_configs/event_socket.conf.xml RED5=red5 RED5_DIR=/usr/share/$RED5 - -TOMCAT_DIR=/var/lib/$TOMCAT_SERVICE LTI_DIR=$TOMCAT_DIR/webapps/lti if [ -f /usr/share/bbb-web/WEB-INF/classes/bigbluebutton.properties ]; then - SERVLET_DIR=/usr/share/bbb-web + SERVLET_DIR=/usr/share/bbb-web else - SERVLET_DIR=/var/lib/tomcat7/webapps/bigbluebutton + SERVLET_DIR=/var/lib/tomcat7/webapps/bigbluebutton fi PROTOCOL=http if [ -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties ]; then - SERVER_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') - if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep bigbluebutton.web.serverURL | grep -q https; then - PROTOCOL=https - fi + SERVER_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') + if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep bigbluebutton.web.serverURL | grep -q https; then + PROTOCOL=https + fi fi PROTOCOL_RTMP=rtmp -FREESWITCH_VARS=/opt/freeswitch/conf/vars.xml -FREESWITCH_EXTERNAL=/opt/freeswitch/conf/sip_profiles/external.xml -FREESWITCH_PID=/opt/freeswitch/run/freeswitch.pid -FREESWITCH_EVENT_SOCKET=/opt/freeswitch/conf/autoload_configs/event_socket.conf.xml -FREESWITCH_INIT_D="/etc/init.d/freeswitch" - RECORD_CONFIG=/usr/local/bigbluebutton/core/scripts/bigbluebutton.yml HTML5_CONFIG_OLD=/usr/share/meteor/bundle/programs/server/assets/app/config/settings-production.json @@ -137,88 +136,64 @@ HTML5_CONFIG=/usr/share/meteor/bundle/programs/server/assets/app/config/settings KURENTO_CONFIG=/usr/local/bigbluebutton/bbb-webrtc-sfu/config/default.yml -BBB_WEB_CONFIG=$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties +BBB_WEB_CONFIG="$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" NGINX_IP=$(cat /etc/nginx/sites-available/bigbluebutton | grep -v '#' | sed -n '/server_name/{s/.*server_name[ ]*//;s/;//;p}' | cut -d' ' -f1 | head -n 1) SIP_CONFIG=/etc/bigbluebutton/nginx/sip.nginx SIP_NGINX_IP=$(cat $SIP_CONFIG | grep -v '#' | sed -n '/proxy_pass/{s/.*proxy_pass http[s]*:\/\///;s/:.*//;p}' | head -n 1) -NCPU=`nproc --all` +NCPU=$(nproc --all) BBB_USER=bigbluebutton -if [ -f /etc/redhat-release ]; then - DISTRIB_ID=centos - SERVLET_LOGS=/usr/share/tomcat/logs - FREESWITCH=freeswitch - FREESWITCH_INIT_D="/etc/init.d/freeswitch" - TOMCAT_SERVICE=tomcat - REDIS_SERVICE=redis.service -else - . /etc/lsb-release # Get value for DISTRIB_ID - SERVLET_LOGS=$TOMCAT_DIR/logs - FREESWITCH=freeswitch - FREESWITCH_INIT_D="/etc/init.d/freeswitch" - TOMCAT_SERVICE=tomcat7 - REDIS_SERVICE=redis-server - - FREESWITCH_VARS=/opt/freeswitch/etc/freeswitch/vars.xml - FREESWITCH_EXTERNAL=/opt/freeswitch/etc/freeswitch/sip_profiles/external.xml - FREESWITCH_PID=/opt/freeswitch/var/run/freeswitch/freeswitch.pid - FREESWITCH_EVENT_SOCKET=/opt/freeswitch/etc/freeswitch/autoload_configs/event_socket.conf.xml -fi - if [ ! -f /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml ]; then - echo "#" - echo "# BigBlueButton does not appear to be installed. Could not" - echo "# locate:" - echo "#" - echo "# /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml" - exit 1 + echo "#" + echo "# BigBlueButton does not appear to be installed. Could not" + echo "# locate:" + echo "#" + echo "# /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml" + exit 1 fi if [ ! -f /var/www/bigbluebutton/client/conf/config.xml ]; then - echo "#" - echo "# BigBlueButton does not appear to be installed. Could not" - echo "# locate:" - echo "#" - echo "# /var/www/bigbluebutton/client/conf/config.xml" - exit 1 + echo "#" + echo "# BigBlueButton does not appear to be installed. Could not" + echo "# locate:" + echo "#" + echo "# /var/www/bigbluebutton/client/conf/config.xml" + exit 1 fi # # We're going to give ^bigbluebutton.web.logoutURL a default value (if undefined) so bbb-conf does not give a warning # if [ -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties ]; then - if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -q ^bigbluebutton.web.logoutURL=$; then - $SUDO sed -i s/^bigbluebutton.web.logoutURL=$/bigbluebutton.web.logoutURL=default/g $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties - fi + if cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -q ^bigbluebutton.web.logoutURL=$; then + $SUDO sed -i s/^bigbluebutton.web.logoutURL=$/bigbluebutton.web.logoutURL=default/g $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties + fi fi -VOICE_CONFERENCE="bbb-voice-freeswitch.xml" - # # Determine IP so it works on multilingual installations # if LANG=c ifconfig | grep -q 'venet0:0'; then - IP=$(ifconfig | grep -v '127.0.0.1' | grep -E "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | tail -1 | cut -d: -f2 | awk '{ print $1}') + IP=$(ifconfig | grep -v '127.0.0.1' | grep -E "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | tail -1 | cut -d: -f2 | awk '{ print $1}') else - IP=$(hostname -I | cut -f1 -d' ') + IP=$(hostname -I | cut -f1 -d' ') fi if [ -z "$IP" ]; then - if [ -f /etc/redhat-release ]; then - IP=$(hostname -I | sed 's/ .*//g') - fi + if [ $DISTRIB_ID == "centos" ]; then + IP=$(hostname -I | sed 's/ .*//g') + fi fi # # Calculate total memory on this server # -MEM=`grep MemTotal /proc/meminfo | awk '{print $2}'` +MEM=$(grep MemTotal /proc/meminfo | awk '{print $2}') MEM=$((MEM/1000)) - # # Check if the function has a value and, if not, print an error message # $1 -- name of value @@ -226,518 +201,394 @@ MEM=$((MEM/1000)) # $3 -- value to check # check_no_value() { - if [ -z $3 ]; then - echo "# Tried to check $1 in" - echo "# $2" - echo "# but value is empty." - exit 1 - fi + if [ -z $3 ]; then + echo "# Tried to check $1 in" + echo "# $2" + echo "# but value is empty." + exit 1 + fi } check_file() { - if [ ! -f $1 ]; then - echo "# File does not exist: $1" - fi + if [ ! -f $1 ]; then + echo "# File does not exist: $1" + fi } print_header() { - if [ ! $HEADER ]; then - echo - echo "** Potential problems described below **" - HEADER=1 - fi -} - -check_root() { - if [ $EUID == 0 ]; then - echo "This operation should not be run as root." - echo - echo "If this operation needs to execute an operation as root, you'll be asked for" - echo "your password to execute the operation using sudo." - exit 1 - fi + if [ ! $HEADER ]; then + echo + echo "** Potential problems described below **" + HEADER=1 + fi } need_root() { - if [ $EUID != 0 ]; then - echo "Need to be root to run this option" - exit 1 - fi + if [ $EUID != 0 ]; then + echo "Need to be root to run this option" + exit 1 + fi } usage() { - echo "BigBlueButton Configuration Utility - Version $BIGBLUEBUTTON_RELEASE" - echo - echo " bbb-conf [options]" - echo - echo "Configuration:" - echo " --version Display BigBlueButton version (packages)" - echo " --setip <IP/hostname> Set IP/hostname for BigBlueButton" - echo " --setsecret <secret> Change the shared secret in bigbluebutton.properties" - echo - echo "Monitoring:" - echo " --check Check configuration files and processes for problems" - echo " --debug Scan the log files for error messages" - echo " --watch Scan the log files for error messages every 2 seconds" - echo " --network View network connections on 80 and 1935 by IP address" - echo " --secret View the URL and shared secret for the server" - echo " --lti View the URL and secret for LTI (if installed)" - echo - echo "Administration:" - echo " --restart Restart BigBlueButton" - echo " --stop Stop BigBlueButton" - echo " --start Start BigBlueButton" - echo " --clean Restart and clean all log files" - echo " --status Display running status of components" - echo " --zip Zip up log files for reporting an error" - - echo + echo "BigBlueButton Configuration Utility - Version $BIGBLUEBUTTON_RELEASE" + echo + echo " bbb-conf [options]" + echo + echo "Configuration:" + echo " --version Display BigBlueButton version (packages)" + echo " --setip <IP/hostname> Set IP/hostname for BigBlueButton" + echo " --setsecret <secret> Change the shared secret in bigbluebutton.properties" + echo + echo "Monitoring:" + echo " --check Check configuration files and processes for problems" + echo " --debug Scan the log files for error messages" + echo " --watch Scan the log files for error messages every 2 seconds" + echo " --network View network connections on 80 and 1935 by IP address" + echo " --secret View the URL and shared secret for the server" + echo " --lti View the URL and secret for LTI (if installed)" + echo + echo "Administration:" + echo " --restart Restart BigBlueButton" + echo " --stop Stop BigBlueButton" + echo " --start Start BigBlueButton" + echo " --clean Restart and clean all log files" + echo " --status Display running status of components" + echo " --zip Zip up log files for reporting an error" + echo } # utility function to make a copy of the conf file check_and_backup () { - # can we write to the configuration file? - if [ ! -w $1 ]; then - echo "Cannot write to $1!" - exit 1 - fi - - # let's see if we need a copy - if [ "$TO_BACKUP" = "Y" ]; then - cp $1 $1.bak - TO_BACKUP="N" - fi + # can we write to the configuration file? + if [ ! -w $1 ]; then + echo "Cannot write to $1!" + exit 1 + fi + + # let's see if we need a copy + if [ "$TO_BACKUP" = "Y" ]; then + cp $1 $1.bak + TO_BACKUP="N" + fi } # 3 paramenter: the file, the variable name, the new value change_var_value () { - check_and_backup $1 - sed -i "s<^[[:blank:]#]*\(${2}\).*<\1=${3}<" $1 + check_and_backup $1 + sed -i "s<^[[:blank:]#]*\(${2}\).*<\1=${3}<" $1 } # same as change_var_value but with quotes change_var_salt() { - check_and_backup $1 - sed -i "s<^[[:blank:]#]*\(${2}\).*<\1="${3}"<" $1 + check_and_backup $1 + sed -i "s<^[[:blank:]#]*\(${2}\).*<\1="${3}"<" $1 } # comment lines matching $2 ($1 is the file) comment () { - check_and_backup $1 - sed -i "s<^[[:blank:]]*\(${2}.*\)<#\1<" $1 + check_and_backup $1 + sed -i "s<^[[:blank:]]*\(${2}.*\)<#\1<" $1 } change_yml_value () { - sed -i "s<^\([[:blank:]#]*\)\(${2}\): .*<\1\2: ${3}<" $1 + sed -i "s<^\([[:blank:]#]*\)\(${2}\): .*<\1\2: ${3}<" $1 } # comment lines matching $2 ($1 is the file) uncomment () { - check_and_backup $1 - sed -i "s<^[#[:blank:]]*\(${2}.*\)<\1<" $1 + check_and_backup $1 + sed -i "s<^[#[:blank:]]*\(${2}.*\)<\1<" $1 } stop_bigbluebutton () { echo "Stopping BigBlueButton" - if command -v systemctl >/dev/null; then - if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then - HTML5="mongod bbb-html5 bbb-webrtc-sfu kurento-media-server" - fi - if [ -f /usr/lib/systemd/system/bbb-webhooks.service ]; then - WEBHOOKS=bbb-webhooks - fi - if [ -f /usr/lib/systemd/system/bbb-transcode-akka.service ]; then - BBB_TRANSCODE_AKKA=bbb-transcode-akka - fi - if [ -f /usr/share/etherpad-lite/settings.json ]; then - ETHERPAD=etherpad - fi - if [ -f /lib/systemd/system/bbb-web.service ]; then - BBB_WEB=bbb-web - fi - - systemctl stop red5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-rap-archive-worker.service bbb-rap-process-worker.service bbb-rap-publish-worker.service bbb-rap-sanity-worker.service bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB - else - /etc/init.d/monit stop - - /etc/init.d/$RED5 stop - /etc/init.d/${TOMCAT_SERVICE} stop - /etc/init.d/nginx stop - - if [ -a $FREESWITCH_PID ]; then - $FREESWITCH_INIT_D stop - fi - - if [ -f /etc/init.d/bbb-office ]; then - /etc/init.d/bbb-office stop - fi - - if [ -f /etc/init.d/bbb-record-core ]; then - /etc/init.d/bbb-record-core stop - fi - - if [ -f /etc/init/bbb-apps-akka.conf ]; then - service bbb-apps-akka stop - fi - if [ -f /etc/init.d/bbb-apps-akka ]; then - /etc/init.d/bbb-apps-akka stop - fi - if [ -f /etc/init/bbb-fsesl-akka.conf ]; then - service bbb-fsesl-akka stop - fi - if [ -f /etc/init.d/bbb-fsesl-akka ]; then - /etc/init.d/bbb-fsesl-akka stop - fi + if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then + HTML5="mongod bbb-html5 bbb-webrtc-sfu kurento-media-server" + fi - if [ -f /etc/init/bbb-transcode-akka.conf ]; then - service bbb-transcode-akka stop - fi - if [ -f /etc/init.d/bbb-transcode-akka ]; then - /etc/init.d/bbb-transcode-akka stop - fi - fi + if [ -f /usr/lib/systemd/system/bbb-webhooks.service ]; then + WEBHOOKS=bbb-webhooks + fi + + if [ -f /usr/lib/systemd/system/bbb-transcode-akka.service ]; then + BBB_TRANSCODE_AKKA=bbb-transcode-akka + fi + + if [ -f /usr/share/etherpad-lite/settings.json ]; then + ETHERPAD=etherpad + fi + + if [ -f /lib/systemd/system/bbb-web.service ]; then + BBB_WEB=bbb-web + fi + + systemctl stop red5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-rap-archive-worker.service bbb-rap-process-worker.service bbb-rap-publish-worker.service bbb-rap-sanity-worker.service bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB } start_bigbluebutton () { - # - # Apply any local configuration options (if exists) - # - if [ -x /etc/bigbluebutton/bbb-conf/apply-config.sh ]; then - echo - echo "Applying updates in /etc/bigbluebutton/bbb-conf/apply-config.sh: " - /etc/bigbluebutton/bbb-conf/apply-config.sh - echo - fi - - if [ -f /opt/freeswitch/var/log/freeswitch/freeswitch.log ]; then - if grep -q "Failure to connect to CORE_DB sofia_reg_external" /opt/freeswitch/var/log/freeswitch/freeswitch.log; then - # See: http://docs.bigbluebutton.org/install/install.html#freeswitch-fails-to-bind-to-ipv4 - echo "Clearing the FreeSWITCH database." - rm -rf /opt/freeswitch/var/lib/freeswitch/db/* - fi - fi - - # Clear any previous registration errors - if [ -f /usr/share/red5/log/sip.log ]; then - sed -i 's/Failed to register with Sip Server/Failed to register with Sip server/g' /usr/share/red5/log/sip.log - fi + # + # Apply any local configuration options (if exists) + # + if [ -x /etc/bigbluebutton/bbb-conf/apply-config.sh ]; then + echo + echo "Applying updates in /etc/bigbluebutton/bbb-conf/apply-config.sh: " + /etc/bigbluebutton/bbb-conf/apply-config.sh + echo + fi - echo "Starting BigBlueButton" - if command -v systemctl >/dev/null; then - if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then - HTML5="mongod bbb-html5 bbb-webrtc-sfu kurento-media-server" - fi - if [ -f /usr/lib/systemd/system/bbb-webhooks.service ]; then - WEBHOOKS=bbb-webhooks - fi - if [ -f /usr/lib/systemd/system/bbb-transcode-akka.service ]; then - BBB_TRANSCODE_AKKA=bbb-transcode-akka - fi - if [ -f /usr/share/etherpad-lite/settings.json ]; then - ETHERPAD=etherpad - fi - if [ -f /lib/systemd/system/bbb-web.service ]; then - BBB_WEB=bbb-web - fi - - systemctl start red5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB - if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then - systemctl start mongod - sleep 3 - systemctl start bbb-html5 - fi - else - $FREESWITCH_INIT_D start - - FREESWITCH_ESL_IP=$(cat $FREESWITCH_EVENT_SOCKET | grep 'name="listen-ip"' | cut -d\" -f4 | awk '{print $1}') - check_no_value listen-ip $FREESWITCH_EVENT_SOCKET $FREESWITCH_ESL_IP - - echo -n "Waiting for FreeSWITCH to start: " - - if [[ ! -z $FREESWITCH_ESL_IP && $DISTRIB_ID != "centos" ]]; then - while ! nc -w 1 $FREESWITCH_ESL_IP 8021 > /dev/null; do - echo -n "." - sleep 1 - done - fi - echo - - /etc/init.d/nginx start - /etc/init.d/$RED5 start - /etc/init.d/${TOMCAT_SERVICE} start - - # monit will start libreoffice and bbb-rap-worker - /etc/init.d/monit start - echo - echo "Note: monit will automatically start bbb-record-core and LibreOffice within 60 seconds." - echo - - BBB_WEB=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') - if [ -f /etc/init/bbb-html5.conf ]; then - echo -n "Waiting for Meteor (HTML5 server) to finish starting up (this may take a minute): " - start bbb-html5 - - if ! wget -q -O - http://$BBB_WEB/html5client/check | grep running > /dev/null; then - while ! wget -q -O - http://$BBB_WEB/html5client/check | grep running > /dev/null; do - echo -n "." - sleep 1 - done - fi + if [ -f /opt/freeswitch/var/log/freeswitch/freeswitch.log ]; then + if grep -q "Failure to connect to CORE_DB sofia_reg_external" /opt/freeswitch/var/log/freeswitch/freeswitch.log; then + # See: http://docs.bigbluebutton.org/install/install.html#freeswitch-fails-to-bind-to-ipv4 + echo "Clearing the FreeSWITCH database." + rm -rf /opt/freeswitch/var/lib/freeswitch/db/* fi + fi - if [ -f /etc/init/bbb-apps-akka.conf ]; then - service bbb-apps-akka start - fi - if [ -f /etc/init.d/bbb-apps-akka ]; then - /etc/init.d/bbb-apps-akka start - fi + # Clear any previous registration errors + if [ -f /usr/share/red5/log/sip.log ]; then + sed -i 's/Failed to register with Sip Server/Failed to register with Sip server/g' /usr/share/red5/log/sip.log + fi - if [ -f /etc/init/bbb-fsesl-akka.conf ]; then - service bbb-fsesl-akka start - fi - if [ -f /etc/init.d/bbb-fsesl-akka ]; then - /etc/init.d/bbb-fsesl-akka start - fi + echo "Starting BigBlueButton" + if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then + HTML5="mongod bbb-html5 bbb-webrtc-sfu kurento-media-server" + fi - if [ -f /etc/init/bbb-transcode-akka.conf ]; then - service bbb-transcode-akka start - fi - if [ -f /etc/init.d/bbb-transcode-akka ]; then - /etc/init.d/bbb-transcode-akka start - fi + if [ -f /usr/lib/systemd/system/bbb-webhooks.service ]; then + WEBHOOKS=bbb-webhooks + fi + if [ -f /usr/lib/systemd/system/bbb-transcode-akka.service ]; then + BBB_TRANSCODE_AKKA=bbb-transcode-akka + fi - # - # At this point the red5 and servlet container applications are starting up. - # - echo -n "Waiting for BigBlueButton to finish starting up (this may take a minute): " + if [ -f /usr/share/etherpad-lite/settings.json ]; then + ETHERPAD=etherpad + fi - check_no_value server_name /etc/nginx/sites-available/bigbluebutton $NGINX_IP + if [ -f /lib/systemd/system/bbb-web.service ]; then + BBB_WEB=bbb-web + fi - #if ! wget http://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then - # echo "Startup unsuccessful: could not connect to http://$BBB_WEB/bigbluebutton/api" - # exit 1 - #fi + systemctl start red5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka $BBB_TRANSCODE_AKKA bbb-fsesl-akka bbb-record-core.timer $HTML5 $WEBHOOKS $ETHERPAD $BBB_WEB + if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then + systemctl start mongod + sleep 3 + systemctl start bbb-html5 fi - # - # Look for Starting up context to be recently added to the log files - # - COUNT=0 - while [ $COUNT -lt 20 ]; do - let COUNT=COUNT+1 - if [ -f $RED5_DIR/log/sip.log ] && [ -f $RED5_DIR/log/bigbluebutton.log ] && [ -f $RED5_DIR/log/red5.log ] && [ -f $RED5_DIR/log/video.log ] && [ -f $RED5_DIR/log/screenshare-slf.log ]; then + # + # Look for Starting up context to be recently added to the log files + # + COUNT=0 + while [ $COUNT -lt 20 ]; do + let COUNT=COUNT+1 + if [ -f $RED5_DIR/log/sip.log ] && [ -f $RED5_DIR/log/bigbluebutton.log ] && [ -f $RED5_DIR/log/red5.log ] && [ -f $RED5_DIR/log/video.log ] && [ -f $RED5_DIR/log/screenshare-slf.log ]; then let COUNT=20 - else + else echo -n "." sleep 1 - fi - done - - - # - # All the log files exist, now check for the text "Starting up context" - # - COUNT=0 - while [ $COUNT -lt 20 ]; do - let COUNT=COUNT+1 - if [ -f $RED5_DIR/log/video.log ]; then - if ! cat $RED5_DIR/log/video.log | tail -n100 | grep -q "Starting up context"; then - echo -n "." - sleep 1 - else - let COUNT=20 + fi + done + + + # + # All the log files exist, now check for the text "Starting up context" + # + COUNT=0 + while [ $COUNT -lt 20 ]; do + let COUNT=COUNT+1 + if [ -f $RED5_DIR/log/video.log ]; then + if ! cat $RED5_DIR/log/video.log | tail -n100 | grep -q "Starting up context"; then + echo -n "." + sleep 1 + else + let COUNT=20 fi - fi - done - - echo - BBB_APPS="sip video bigbluebutton screenshare-slf" - for bbb_app in $BBB_APPS ; do - if [ -a $RED5_DIR/log/$bbb_app.log ]; then - if ! grep -q "Starting up context" $RED5_DIR/log/$bbb_app.log; then - echo "# $bbb_app may not have started properly" - fi - else - echo "# $RED5_DIR/log/$bbb_app.log not found" - fi - done - + fi + done + + echo + BBB_APPS="sip video bigbluebutton screenshare-slf" + for bbb_app in $BBB_APPS ; do + if [ -a $RED5_DIR/log/$bbb_app.log ]; then + if ! grep -q "Starting up context" $RED5_DIR/log/$bbb_app.log; then + echo "# $bbb_app may not have started properly" + fi + else + echo "# $RED5_DIR/log/$bbb_app.log not found" + fi + done } display_bigbluebutton_status () { - if command -v systemctl >/dev/null; then - units="red5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-transcode-akka bbb-fsesl-akka" - - if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then - units="$units mongod bbb-html5 bbb-webrtc-sfu kurento-media-server" - fi + units="red5 $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-transcode-akka bbb-fsesl-akka" - if [ -f /lib/systemd/system/bbb-web.service ]; then - units="$units bbb-web" - fi + if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then + units="$units mongod bbb-html5 bbb-webrtc-sfu kurento-media-server" + fi - line='——————————————————————►' - for unit in $units; do - status=$(systemctl is-active "$unit") - if [ "$status" = "active" ]; then - printf "%s %s [✔ - $status]\n" $unit "${line:${#unit}}" - else - printf "%s %s [✘ - $status]\n" $unit "${line:${#unit}}" - fi - done - else - /etc/init.d/nginx status - /etc/init.d/$RED5 status - /etc/init.d/${TOMCAT_SERVICE} status + if [ -f /lib/systemd/system/bbb-web.service ]; then + units="$units bbb-web" fi + + line='——————————————————————►' + for unit in $units; do + status=$(systemctl is-active "$unit") + if [ "$status" = "active" ]; then + printf "%s %s [✔ - $status]\n" $unit "${line:${#unit}}" + else + printf "%s %s [✘ - $status]\n" $unit "${line:${#unit}}" + fi + done } if [ $# -eq 0 ]; then - usage - exit 1 + usage + exit 1 fi # Parse the parameters while [ $# -gt 0 ]; do - if [ "$1" = "-stop" -o "$1" = "--stop" ]; then - need_root - stop_bigbluebutton - exit 0 - fi - - if [ "$1" = "-start" -o "$1" = "--start" ]; then - need_root - start_bigbluebutton - exit 0 - fi - - if [ "$1" = "-check" -o "$1" = "--check" -o "$1" = "-c" ]; then - CHECK=1 - shift;shift - continue - fi - - if [ "$1" = "--version" -o "$1" = "-version" -o "$1" = "-v" ]; then - VERSION=1 - shift - continue - fi - - if [ "$1" = "--debug" -o "$1" = "-debug" -o "$1" = "-d" ]; then - DEBUG=1 - shift - continue - fi - - if [ "$1" = "--clean" -o "$1" = "-clean" ]; then - CLEAN=1 - shift - continue - fi - - if [ "$1" = "--watch" -o "$1" = "-watch" -o "$1" = "-w" ]; then - WATCH=1 - shift - continue - fi - - if [ "$1" = "--network" -o "$1" = "-network" -o "$1" = "-n" ]; then - NETWORK=1 - shift - continue - fi - - if [ "$1" = "--zip" -o "$1" = "-zip" -o "$1" = "-z" ]; then - ZIP=1 - shift - continue - fi - - if [ "$1" = "--status" -o "$1" = "-status" ]; then - display_bigbluebutton_status - exit 0 + if [ "$1" = "-stop" -o "$1" = "--stop" ]; then + need_root + stop_bigbluebutton + exit 0 + fi + + if [ "$1" = "-start" -o "$1" = "--start" ]; then + need_root + start_bigbluebutton + exit 0 + fi + + if [ "$1" = "-check" -o "$1" = "--check" -o "$1" = "-c" ]; then + CHECK=1 + shift;shift + continue + fi + + if [ "$1" = "--version" -o "$1" = "-version" -o "$1" = "-v" ]; then + VERSION=1 + shift + continue + fi + + if [ "$1" = "--debug" -o "$1" = "-debug" -o "$1" = "-d" ]; then + DEBUG=1 + shift + continue + fi + + if [ "$1" = "--clean" -o "$1" = "-clean" ]; then + CLEAN=1 + shift + continue + fi + + if [ "$1" = "--watch" -o "$1" = "-watch" -o "$1" = "-w" ]; then + WATCH=1 + shift + continue + fi + + if [ "$1" = "--network" -o "$1" = "-network" -o "$1" = "-n" ]; then + NETWORK=1 + shift + continue + fi + + if [ "$1" = "--zip" -o "$1" = "-zip" -o "$1" = "-z" ]; then + ZIP=1 + shift + continue + fi + + if [ "$1" = "--status" -o "$1" = "-status" ]; then + display_bigbluebutton_status + exit 0 + fi + + if [ "$1" = "--restart" -o "$1" = "-restart" ]; then + RESTART=1 + shift + continue + fi + + # + # all other parameters requires at least 1 argument + # + + if [ "$1" = "-setip" -o "$1" = "--setip" ]; then + HOST="${2}" + if [ -z "$HOST" ]; then + echo "HOST IP=$IP" fi - if [ "$1" = "--restart" -o "$1" = "-restart" ]; then - RESTART=1 - shift - continue + if echo $HOST|grep -q ":"; then + HOST=$(echo ${2}|cut -d: -f1) fi + shift; shift + continue + fi + + if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" -o "$1" = "--secret" -o "$1" = "-secret" -o "$1" = "--setsecret" ]; then + SECRET="${2}" + if [ -z "$SECRET" ]; then + BBB_WEB_URL=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') + SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2); + echo + echo " URL: $BBB_WEB_URL/bigbluebutton/" + echo " Secret: $SECRET" + echo + echo " Link to the API-Mate:" + echo " http://mconf.github.io/api-mate/#server=$BBB_WEB_URL/bigbluebutton/&sharedSecret=$SECRET" + echo + exit 0 + fi + shift; shift + continue + fi - # - # all other parameters requires at least 1 argument - # - - if [ "$1" = "-setip" -o "$1" = "--setip" ]; then - HOST="${2}" - if [ -z "$HOST" ]; then - echo "HOST IP=$IP" - fi - - if echo $HOST|grep -q ":"; then - HOST=`echo ${2}|cut -d: -f1` - fi - shift; shift - continue - fi - - if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" -o "$1" = "--secret" -o "$1" = "-secret" -o "$1" = "--setsecret" ]; then - SECRET="${2}" - if [ -z "$SECRET" ]; then - BBB_WEB_URL=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') - SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2); - echo - echo " URL: $BBB_WEB_URL/bigbluebutton/" - echo " Secret: $SECRET" - echo - echo " Link to the API-Mate:" - echo " http://mconf.github.io/api-mate/#server=$BBB_WEB_URL/bigbluebutton/&sharedSecret=$SECRET" - echo - exit 0 - fi - shift; shift - continue - fi - - if [ "$1" = "--lti" -o "$1" = "-lti" ]; then - if [ -z "$SECRET" ]; then - if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then - LTI_URL='http://'$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiEndPoint/{s/^.*=//;p}')'/lti/tool' - CUSTOMER=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiConsumer/{s/^.*=//;s/:.*//p}') - SECRET=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiConsumer/{s/^[^:]*://;p}') - echo - echo " URL: $LTI_URL" - echo " Customer: $CUSTOMER" - echo " Secret: $SECRET" - echo - ICON_URL=$( echo $LTI_URL | sed 's/tool/images\/icon.ico/') - echo " Icon URL: $ICON_URL" - echo - echo - exit 0 - fi - fi - shift; shift - continue - fi - - usage - exit 1 + if [ "$1" = "--lti" -o "$1" = "-lti" ]; then + if [ -z "$SECRET" ]; then + if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then + LTI_URL='http://'$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiEndPoint/{s/^.*=//;p}')'/lti/tool' + CUSTOMER=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiConsumer/{s/^.*=//;s/:.*//p}') + SECRET=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiConsumer/{s/^[^:]*://;p}') + echo + echo " URL: $LTI_URL" + echo " Customer: $CUSTOMER" + echo " Secret: $SECRET" + echo + ICON_URL=$( echo $LTI_URL | sed 's/tool/images\/icon.ico/') + echo " Icon URL: $ICON_URL" + echo + echo + exit 0 + fi + fi + shift; shift + continue + fi + + usage + exit 1 done # -# Version +# Version # -if [ $VERSION ]; then - echo - dpkg -l | grep bbb - exit 0 +if [[ $VERSION ]]; then + echo + dpkg -l | grep bbb + exit 0 fi @@ -745,249 +596,198 @@ fi # Set Shared Secret # -if [ $SECRET ]; then - need_root - change_var_salt ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties securitySalt $SECRET +if [[ $SECRET ]]; then + need_root + change_var_salt ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties securitySalt $SECRET - if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee ]; then - sed -i "s|\(^[ \t]*config.bbb.sharedSecret[ =]*\).*|\1\"$SECRET\"|g" /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee - fi - if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.js ]; then - sed -i "s|\(^[ \t]*config.bbb.sharedSecret[ =]*\).*|\1\"$SECRET\"|g" /usr/local/bigbluebutton/bbb-webhooks/config_local.js + if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee ]; then + sed -i "s|\(^[ \t]*config.bbb.sharedSecret[ =]*\).*|\1\"$SECRET\"|g" /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee + fi + if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.js ]; then + sed -i "s|\(^[ \t]*config.bbb.sharedSecret[ =]*\).*|\1\"$SECRET\"|g" /usr/local/bigbluebutton/bbb-webhooks/config_local.js + fi + + if [ -f /usr/local/bigbluebutton/bbb-webhooks/extra/post_catcher.js ]; then + sed -i "s|\(^[ \t]*var shared_secret[ =]*\)[^;]*|\1\"$SECRET\"|g" /usr/local/bigbluebutton/bbb-webhooks/extra/post_catcher.js + fi + + if [ -f /usr/share/bbb-apps-akka/conf/application.conf ]; then + sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SECRET\"/g" /usr/share/bbb-apps-akka/conf/application.conf + fi + + echo "Changed BigBlueButton's shared secret to $SECRET" + echo + echo "You must restart BigBlueButton for the changes to take effect" + echo " $SUDO bbb-conf --restart" + echo +fi + +check_configuration() { + # + # Check that freeswtich ESL matches the value in bigbluebutton.properties + # + if [ -f $FREESWITCH_EVENT_SOCKET ]; then + FREESWITCH_ESL_IP=$(cat $FREESWITCH_EVENT_SOCKET | grep 'name="listen-ip"' | cut -d\" -f4 | awk '{print $1}') + check_no_value event_socket $FREESWITCH_EVENT_SOCKET $FREESWITCH_ESL_IP + + ESL_HOST=$(cat /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties | grep -v '#' | grep esl.host | sed 's/freeswitch.esl.host=//g') + check_no_value esl.host /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties $ESL_HOST + + if [ "$FREESWITCH_ESL_IP" != "::" ]; then + if [ "$FREESWITCH_ESL_IP" != "$ESL_HOST" ]; then + echo + echo "# The values for listen-ip in " + echo "# $FREESWITCH_EVENT_SOCKET" + echo "# do not match the settings for esl.host in" + echo "# /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties" + echo + fi fi + fi + + # + # Check if BigBlueButton is defined in Nginx + # + if [ ! -L /etc/nginx/sites-enabled/bigbluebutton ]; then + echo "# Nginx: BigBlueButton appears to be disabled" + echo " - no symbolic link in /etc/nginx/sites-enabled/bigbluebutton to /etc/nginx/sites-available/bigbluebutton " + fi - if [ -f /usr/local/bigbluebutton/bbb-webhooks/extra/post_catcher.js ]; then - sed -i "s|\(^[ \t]*var shared_secret[ =]*\)[^;]*|\1\"$SECRET\"|g" /usr/local/bigbluebutton/bbb-webhooks/extra/post_catcher.js - fi + # + # Look for properties with no values set + # + CONFIG_FILES="$RED5_DIR/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties \ + ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties \ + $RED5_DIR/webapps/sip/WEB-INF/bigbluebutton-sip.properties" - if [ -f /usr/share/bbb-apps-akka/conf/application.conf ]; then - sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SECRET\"/g" \ - /usr/share/bbb-apps-akka/conf/application.conf + for file in $CONFIG_FILES ; do + if [ ! -f $file ]; then + echo "# Error: File not found: $file" + else + if cat $file | grep -v redis.pass | grep -q "^[^=]*=[ ]*$"; then + echo "# The following properties in $file have no value:" + echo "# $(grep '^[^=#]*=[ ]*$' $file | sed 's/=//g')" + fi fi + done + + # + # Check that the supporting applications are installed + # + VARFolder=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep swfToolsDir | cut -d= -f2) + files="pdf2swf jpeg2swf png2swf" + for file in $files; do + if [ ! -x $VARFolder/$file ]; then + echo "# $file is not installed in $VARFolder" + fi + done - echo "Changed BigBlueButton's shared secret to $SECRET" - echo - echo "You must restart BigBlueButton for the changes to take effect" - echo " $SUDO bbb-conf --restart" - echo -fi + VARFolder=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep imageMagickDir | cut -d= -f2) + if [ ! -x $VARFolder/convert ]; then + echo "# ImageMagick's convert is not installed in $VARFolder" + fi -# -# Setup samba -# + # + # Check if the IP resolves to a different host + # + check_no_value server_name /etc/nginx/sites-available/bigbluebutton $NGINX_IP + + if which host > /dev/null 2>&1; then + HOSTS=$(which host) + if [ $HOSTS ]; then + HOSTS=$($HOSTS $NGINX_IP | awk '{ print $4 }' | head -n 1) + fi + fi + + if [ "$IP" != "$NGINX_IP" ]; then + if [ "$IP" != "$HOSTS" ]; then + echo "# IP does not match:" + echo "# IP from ifconfig: $IP" + echo "# /etc/nginx/sites-available/bigbluebutton: $NGINX_IP" + fi + fi -if [ $SAMBA ]; then - check_root + if [ -f ${SERVLET_DIR}/demo/bbb_api_conf.jsp ]; then # - # Instal Samba + # Make sure the shared secret for the API matches the server # - if ! dpkg-query -s samba > /dev/null 2>&1; then - $SUDO apt-get install -y --force-yes samba ant + SECRET_PROPERTIES=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}') + SECRET_DEMO=$(cat ${SERVLET_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | tr -d '\r' | sed -n '/salt[ ]*=/{s/.*=[ ]*"//;s/".*//g;p}') + + if [ "$SECRET_PROPERTIES" != "$SECRET_DEMO" ]; then + echo "# Warning: API Shared Secret mismatch: " + echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties = $SECRET_PROPERTIES" + echo "# ${SERVLET_DIR}/demo/bbb_api_conf.jsp = $SECRET_DEMO" + echo fi - # - # Add a share to samba - # - if ! grep -q $USER /etc/samba/smb.conf; then - - echo "; -; BigBlueButton: Share the development directory -[$USER] - comment = BigBlueButton Development share - path = /home/$USER - browseable = yes - read only = no - create mask = 0755 - directory mask = 0775 - guest ok = yes - force user = $USER -" | $SUDO tee -a /etc/samba/smb.conf > /dev/null 2>&1 - - $SUDO /etc/init.d/smbd restart - - echo " -You can now access your development folder through: - - \\\\${IP}\\$USER - -If you are running a development environment on Windows (such as using Eclipse or FlexBuilder), -you can map the above path to a drive letter. -" - else - echo "Already detected a definition for $USER in /etc/samba/smb.conf" - echo "No changes were made to /etc/samba/smb.conf" + API_IP=$(cat ${TOMCAT_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | sed -n '/String BigBlueButtonURL/{s/.*http[s]*:\/\///;s/\/.*//;p}' | tr -d '\015') + if [ "$IP" != "$API_IP" ]; then + echo "# Warning: API URL IPs do not match host:" + echo "#" + echo "# IP from ifconfig: $IP" + echo "# ${TOMCAT_DIR}/demo/bbb_api_conf.jsp: $API_IP" + echo fi -fi + fi + BBB_SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}') -check_configuration() { - # - # Check that freeswtich ESL matches the value in bigbluebutton.properties - # - if [ -f $FREESWITCH_EVENT_SOCKET ]; then - FREESWITCH_ESL_IP=$(cat $FREESWITCH_EVENT_SOCKET | grep 'name="listen-ip"' | cut -d\" -f4 | awk '{print $1}') - check_no_value event_socket $FREESWITCH_EVENT_SOCKET $FREESWITCH_ESL_IP - - ESL_HOST=$(cat /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties | grep -v '#' | grep esl.host | sed 's/freeswitch.esl.host=//g') - check_no_value esl.host /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties $ESL_HOST - - if [ "$FREESWITCH_ESL_IP" != "::" ]; then - if [ "$FREESWITCH_ESL_IP" != "$ESL_HOST" ]; then - echo - echo "# The values for listen-ip in " - echo "# $FREESWITCH_EVENT_SOCKET" - echo "# do not match the settings for esl.host in" - echo "# /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties" - echo - fi - fi - fi - - - # - # Check if BigBlueButton is defined in Nginx - # - if [ ! -L /etc/nginx/sites-enabled/bigbluebutton ]; then - echo "# Nginx: BigBlueButton appears to be disabled" - echo " - no symbolic link in /etc/nginx/sites-enabled/bigbluebutton to /etc/nginx/sites-available/bigbluebutton " - fi - - # - # Look for properties with no values set - # - CONFIG_FILES="$RED5_DIR/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties \ - ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties \ - $RED5_DIR/webapps/sip/WEB-INF/bigbluebutton-sip.properties" - - for file in $CONFIG_FILES ; do - if [ ! -f $file ]; then - echo "# Error: File not found: $file" - else - if cat $file | grep -v redis.pass | grep -q "^[^=]*=[ ]*$"; then - echo "# The following properties in $file have no value:" - echo "# $(grep '^[^=#]*=[ ]*$' $file | sed 's/=//g')" - fi - fi - done - - # - # Check that the supporting applications are installed - # - VARFolder=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep swfToolsDir | cut -d= -f2) - files="pdf2swf jpeg2swf png2swf" - for file in $package; do - if [ ! -x $VARFolder/$file ]; then - echo "# $file is not installed in $VARFolder" - fi - done - - VARFolder=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep imageMagickDir | cut -d= -f2) - if [ ! -x $VARFolder/convert ]; then - echo "# ImageMagick's convert is not installed in $VARFolder" - fi - - # - # Check if the IP resolves to a different host - # - check_no_value server_name /etc/nginx/sites-available/bigbluebutton $NGINX_IP - - if which host > /dev/null 2>&1; then - HOSTS=$(which host) - if [ $HOSTS ]; then - HOSTS=`$HOSTS $NGINX_IP | awk '{ print $4 }' | head -n 1` - fi - fi - - if [ "$IP" != "$NGINX_IP" ]; then - if [ "$IP" != "$HOSTS" ]; then - echo "# IP does not match:" - echo "# IP from ifconfig: $IP" - echo "# /etc/nginx/sites-available/bigbluebutton: $NGINX_IP" - fi - fi - - - if [ -f ${SERVLET_DIR}/demo/bbb_api_conf.jsp ]; then - # - # Make sure the shared secret for the API matches the server - # - SECRET_PROPERTIES=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}') - SECRET_DEMO=$(cat ${SERVLET_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | tr -d '\r' | sed -n '/salt[ ]*=/{s/.*=[ ]*"//;s/".*//g;p}') - - if [ "$SECRET_PROPERTIES" != "$SECRET_DEMO" ]; then - echo "# Warning: API Shared Secret mismatch: " - echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties = $SECRET_PROPERTIES" - echo "# ${SERVLET_DIR}/demo/bbb_api_conf.jsp = $SECRET_DEMO" - echo - fi - - API_IP=$(cat ${TOMCAT_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | sed -n '/String BigBlueButtonURL/{s/.*http[s]*:\/\///;s/\/.*//;p}' | tr -d '\015') - if [ "$IP" != "$API_IP" ]; then - echo "# Warning: API URL IPs do not match host:" - echo "#" - echo "# IP from ifconfig: $IP" - echo "# ${TOMCAT_DIR}/demo/bbb_api_conf.jsp: $API_IP" - echo - fi - fi - - BBB_SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}') - - if [ -f /usr/lib/systemd/system/bbb-webhooks.service ]; then - if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.js ]; then - WEBHOOKS_SECRET=$(cat /usr/local/bigbluebutton/bbb-webhooks/config_local.js | grep '^[ \t]*config.bbb.sharedSecret[ =]*' | cut -d '"' -f2) - WEBHOOKS_CONF=/usr/local/bigbluebutton/bbb-webhooks/config_local.js - fi - if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee ]; then - WEBHOOKS_SECRET=$(cat /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee | grep '^[ \t]*config.bbb.sharedSecret[ =]*' | cut -d '"' -f2) - WEBHOOKS_CONF=/usr/local/bigbluebutton/bbb-webhooks/config_local.coffee - fi + if [ -f /usr/lib/systemd/system/bbb-webhooks.service ]; then + if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.js ]; then + WEBHOOKS_SECRET=$(cat /usr/local/bigbluebutton/bbb-webhooks/config_local.js | grep '^[ \t]*config.bbb.sharedSecret[ =]*' | cut -d '"' -f2) + WEBHOOKS_CONF=/usr/local/bigbluebutton/bbb-webhooks/config_local.js + fi + if [ -f /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee ]; then + WEBHOOKS_SECRET=$(cat /usr/local/bigbluebutton/bbb-webhooks/config_local.coffee | grep '^[ \t]*config.bbb.sharedSecret[ =]*' | cut -d '"' -f2) + WEBHOOKS_CONF=/usr/local/bigbluebutton/bbb-webhooks/config_local.coffee + fi - if [ "$BBB_SECRET" != "$WEBHOOKS_SECRET" ]; then - echo "# Warning: Webhooks API Shared Secret mismatch: " - echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties = $BBB_SECRET" - echo "# $WEBHOOKS_CONF = $WEBHOOKS_SECRET" - echo - fi + if [ "$BBB_SECRET" != "$WEBHOOKS_SECRET" ]; then + echo "# Warning: Webhooks API Shared Secret mismatch: " + echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties = $BBB_SECRET" + echo "# $WEBHOOKS_CONF = $WEBHOOKS_SECRET" + echo + fi - WEBHOOKS_PROXY_PORT=$(cat /etc/bigbluebutton/nginx/webhooks.nginx | grep -v '#' | grep '^[ \t]*proxy_pass[ \t]*' | sed 's|.*http[s]\?://[^:]*:\([^;]*\);.*|\1|g') - WEBHOOKS_APP_PORT=$(cat $WEBHOOKS_CONF | grep config.server.port | sed "s/.*config.server.port[ =\"]*//g" | sed 's/[;\"]*//g') + WEBHOOKS_PROXY_PORT=$(cat /etc/bigbluebutton/nginx/webhooks.nginx | grep -v '#' | grep '^[ \t]*proxy_pass[ \t]*' | sed 's|.*http[s]\?://[^:]*:\([^;]*\);.*|\1|g') + WEBHOOKS_APP_PORT=$(cat $WEBHOOKS_CONF | grep config.server.port | sed "s/.*config.server.port[ =\"]*//g" | sed 's/[;\"]*//g') - if [ "$WEBHOOKS_PROXY_PORT" != "$WEBHOOKS_APP_PORT" ]; then - echo "# Warning: Webhooks port mismatch: " - echo "# /etc/bigbluebutton/nginx/webhooks.nginx = $WEBHOOKS_PROXY_PORT" - echo "# $WEBHOOKS_CONF = $WEBHOOKS_APP_PORT" - echo - fi + if [ "$WEBHOOKS_PROXY_PORT" != "$WEBHOOKS_APP_PORT" ]; then + echo "# Warning: Webhooks port mismatch: " + echo "# /etc/bigbluebutton/nginx/webhooks.nginx = $WEBHOOKS_PROXY_PORT" + echo "# $WEBHOOKS_CONF = $WEBHOOKS_APP_PORT" + echo fi + fi - if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then - LTI_SECRET=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | tr -d '\r' | sed -n '/^bigbluebuttonSalt/{s/.*=//;p}') - BBB_SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}') - - if [ "$LTI_SECRET" != "$BBB_SECRET" ]; then - echo "# Warning: LTI shared secret mismatch:" - echo "# ${LTI_DIR}/WEB-INF/classes/lti-config.properties = $LTI_SECRET" - echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties = $BBB_SECRET" - echo - fi - fi - - PROTOCOL=$(cat /etc/bigbluebutton/nginx/sip.nginx | grep -v \# | sed -n '/proxy_pass/{s/.*proxy_pass [ ]*//;s/:.*//;p}' | head -n 1) - if [[ $PROTOCOL == "https" ]]; then - if ! grep wss-binding $FREESWITCH_EXTERNAL > /dev/null; then - echo "# Warning: Websockets is using HTTPS in /etc/bigbluebutton/nginx/sip.nginx" - echo "# but no definition for wss-binding found in " - echo "#" - echo "# $FREESWITCH_EXTERNAL" - echo - fi - - if [ -f $HTML5_CONFIG_OLD ]; then - if grep \"enableListenOnly\".*true $HTML5_CONFIG_OLD > /dev/null; then - if ! grep -q ws-binding $FREESWITCH_EXTERNAL ; then + if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then + LTI_SECRET=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | tr -d '\r' | sed -n '/^bigbluebuttonSalt/{s/.*=//;p}') + BBB_SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}') + + if [ "$LTI_SECRET" != "$BBB_SECRET" ]; then + echo "# Warning: LTI shared secret mismatch:" + echo "# ${LTI_DIR}/WEB-INF/classes/lti-config.properties = $LTI_SECRET" + echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties = $BBB_SECRET" + echo + fi + fi + + SIP_PROTOCOL=$(cat /etc/bigbluebutton/nginx/sip.nginx | grep -v \# | sed -n '/proxy_pass/{s/.*proxy_pass [ ]*//;s/:.*//;p}' | head -n 1) + if [[ $SIP_PROTOCOL == "https" ]]; then + if ! grep wss-binding $FREESWITCH_EXTERNAL > /dev/null; then + echo "# Warning: Websockets is using HTTPS in /etc/bigbluebutton/nginx/sip.nginx" + echo "# but no definition for wss-binding found in " + echo "#" + echo "# $FREESWITCH_EXTERNAL" + echo + fi + + if [ -f $HTML5_CONFIG_OLD ]; then + if grep \"enableListenOnly\".*true $HTML5_CONFIG_OLD > /dev/null; then + if ! grep -q ws-binding $FREESWITCH_EXTERNAL ; then echo "# Warning: You have enabled listen-only audio via Kurento" echo "# but FreeSWITCH is not listening on port 5066. You should edit " echo "#" @@ -995,607 +795,629 @@ check_configuration() { echo "#" echo "# and add a line that enables ws-binding on port 5066." echo + fi fi - fi fi + fi - fi - - if [ $DISTRIB_ID != "centos" ]; then - libreoffice_version=`dpkg-query -W --showformat='${Version}\n' libreoffice | sed 's/.*://g' | sed 's/\.[^\.]*$//g'` - if [[ "$libreoffice_version" > 1.0 ]]; then - if [[ "$libreoffice_version" < 4.4 ]]; then - echo "# Warning: Detected your running an older version of LibreOffice: $libreoffice_version" - echo "#" - echo "# Recommend updated to the latest version of LibreOffice 4.x (4.4 or later)" - echo - fi - fi - fi - - if [ "$(ls -ld /var/bigbluebutton/screenshare | cut -d' ' -f3)" != "red5" ]; then - echo "# Warning: Detected the directory" - echo "# /var/bigbluebutton/screenshare" - echo "# is not owned by red5" + if [ $DISTRIB_ID != "centos" ]; then + libreoffice_version=$(dpkg-query -W --showformat='${Version}\n' libreoffice | sed 's/.*://g' | sed 's/\.[^\.]*$//g') + if [[ "$libreoffice_version" > 1.0 ]]; then + if [[ "$libreoffice_version" < 4.4 ]]; then + echo "# Warning: Detected your running an older version of LibreOffice: $libreoffice_version" + echo "#" + echo "# Recommend updated to the latest version of LibreOffice 4.x (4.4 or later)" + echo + fi fi - - if [ "$(ls -ld /usr/share/red5/webapps/video/streams | cut -d' ' -f3)" != "red5" ]; then - echo "# Warning: Detected the directory" - echo "# /usr/share/red5/webapps/video/streams" - echo "# is not owned by red5" + fi + + if [ "$(ls -ld /var/bigbluebutton/screenshare | cut -d' ' -f3)" != "red5" ]; then + echo "# Warning: Detected the directory" + echo "# /var/bigbluebutton/screenshare" + echo "# is not owned by red5" + fi + + if [ "$(ls -ld /usr/share/red5/webapps/video/streams | cut -d' ' -f3)" != "red5" ]; then + echo "# Warning: Detected the directory" + echo "# /usr/share/red5/webapps/video/streams" + echo "# is not owned by red5" + fi + + if [ "$(ls -ld /var/freeswitch/meetings | cut -d' ' -f3)" != "freeswitch" ]; then + echo "# Warning: Detected the directory" + echo "# /var/freeswitch/meetings" + echo "# is not owned by freeswitch" + fi + + if [ "$(ls -ld /var/bigbluebutton | cut -d' ' -f3)" != $BBB_USER ]; then + echo "# Warning: Detected the directory" + echo "# /var/bigbluebutton" + echo "# is not owned by $BBB_USER" + fi + + if [ $PROTOCOL == "https" ]; then + if ! grep jnlpUrl /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties | grep -q https; then + echo "# Warning: Detected the value for jnlpUrl is not configured for HTTPS" + echo "# /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties" + echo "#" fi - - if [ "$(ls -ld /var/freeswitch/meetings | cut -d' ' -f3)" != "freeswitch" ]; then - echo "# Warning: Detected the directory" - echo "# /var/freeswitch/meetings" - echo "# is not owned by freeswitch" + fi + + GUEST_POLICY=$(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^defaultGuestPolicy/{s/.*=//;p}') + if [ "$GUEST_POLICY" == "ASK_MODERATOR" ]; then + echo + echo "# Warning: defaultGuestPolicy is set to ASK_MODERATOR in" + echo "# $BBB_WEB_CONFIG" + echo "# This is not yet supported yet the HTML5 client." + echo "#" + echo "# To revert it to ALWAYS_ALLOW, see" + echo "#" + echo "# $SUDO sed -i s/^defaultGuestPolicy=.*$/defaultGuestPolicy=ALWAYS_ALLOW/g $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" + echo "#" + echo + fi + + if [ -f $HTML5_CONFIG ]; then + SVG_IMAGES_REQUIRED=$(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^svgImagesRequired/{s/.*=//;p}') + if [ "$SVG_IMAGES_REQUIRED" != "true" ]; then + echo + echo "# Warning: You have the HTML5 client installed but in" + echo "#" + echo "# $BBB_WEB_CONFIG" + echo "#" + echo "# the setting for svgImagesRequired is false. To fix, run the commnad" + echo "#" + echo "# sed -i 's/^svgImagesRequired=.*/svgImagesRequired=true/' $BBB_WEB_CONFIG " + echo "#" fi + fi +} + + +check_state() { + echo + print_header + check_configuration - if [ "$(ls -ld /var/bigbluebutton | cut -d' ' -f3)" != $BBB_USER ]; then - echo "# Warning: Detected the directory" - echo "# /var/bigbluebutton" - echo "# is not owned by $BBB_USER" + # + # Check for potential problems in the BigBlueButton configuration + # + + RUNNING_APPS="" + NOT_RUNNING_APPS="" + + if ! ps aux | grep -v grep | grep 'org.red5.server.Bootstrap' > /dev/null; then + print_header + NOT_RUNNING_APPS="${NOT_RUNNING_APPS} red5" + else + RUNNING_APPS="${RUNNING_APPS} red5" + fi + + if [[ -a $FREESWITCH_PID ]]; then + if ! ps aux | grep -v grep | grep '[/]opt/freeswitch/bin/freeswitch' > /dev/null; then + print_header + NOT_RUNNING_APPS="${NOT_RUNNING_APPS} freeswitch" + else + RUNNING_APPS="${RUNNING_APPS} freeswitch" fi + fi - if [ $PROTOCOL == "https" ]; then - if ! grep jnlpUrl /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties | grep -q https; then - echo "# Warning: Detected the value for jnlpUrl is not configured for HTTPS" - echo "# /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties" - echo "#" - fi + if ! ps aux | grep -v grep | grep '[/]usr/sbin/nginx' > /dev/null; then + print_header + NOT_RUNNING_APPS="${NOT_RUNNING_APPS} Nginx" + else + RUNNING_APPS="${RUNNING_APPS} Nginx" + fi + + if ! netstat -ant | grep '8090' > /dev/null; then + print_header + NOT_RUNNING_APPS="${NOT_RUNNING_APPS} ${TOMCAT_SERVICE} or grails" + else + if ps aux | ps -aef | grep -v grep | grep grails | grep run-app > /dev/null; then + print_header + RUNNING_APPS="${RUNNING_APPS} Grails" + echo "# ${TOMCAT_SERVICE}: noticed you are running grails run-app instead of ${TOMCAT_SERVICE}" + else + RUNNING_APPS="${RUNNING_APPS} ${TOMCAT_SERVICE}" fi + fi - GUEST_POLICY=$(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^defaultGuestPolicy/{s/.*=//;p}') - if [ "$GUEST_POLICY" == "ASK_MODERATOR" ]; then - echo - echo "# Warning: defaultGuestPolicy is set to ASK_MODERATOR in" - echo "# $BBB_WEB_CONFIG" - echo "# This is not yet supported yet the HTML5 client." - echo "#" - echo "# To revert it to ALWAYS_ALLOW, see" - echo "#" - echo "# $SUDO sed -i s/^defaultGuestPolicy=.*$/defaultGuestPolicy=ALWAYS_ALLOW/g $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" - echo "#" - echo - fi - if [ -f $HTML5_CONFIG ]; then - SVG_IMAGES_REQUIRED=$(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^svgImagesRequired/{s/.*=//;p}') - if [ "$SVG_IMAGES_REQUIRED" != "true" ]; then - echo - echo "# Warning: You have the HTML5 client installed but in" - echo "#" - echo "# $BBB_WEB_CONFIG" - echo "#" - echo "# the setting for svgImagesRequired is false. To fix, run the commnad" - echo "#" - echo "# sed -i 's/^svgImagesRequired=.*/svgImagesRequired=true/' $BBB_WEB_CONFIG " - echo "#" - fi - fi -} + if ! netstat -ant | grep '8100' > /dev/null; then + print_header + NOT_RUNNING_APPS="${NOT_RUNNING_APPS} LibreOffice" + else + RUNNING_APPS="${RUNNING_APPS} LibreOffice" + fi + if ! ps aux | grep -v grep | grep '[/]usr/[s]*bin/redis-server' > /dev/null; then + print_header + NOT_RUNNING_APPS="${NOT_RUNNING_APPS} redis-server" + else + RUNNING_APPS="${RUNNING_APPS} redis-server" + fi -check_state() { - echo - print_header - check_configuration - - # - # Check for potential problems in the BigBlueButton configuration - # - - RUNNING_APPS="" - NOT_RUNNING_APPS="" - - if ! ps aux | grep -v grep | grep 'org.red5.server.Bootstrap' > /dev/null; then - print_header - NOT_RUNNING_APPS="${NOT_RUNNING_APPS} red5" - else - RUNNING_APPS="${RUNNING_APPS} red5" - fi - - if [ -a $FREESWITCH_PID ]; then - if ! ps aux | grep -v grep | grep '[/]opt/freeswitch/bin/freeswitch' > /dev/null; then - print_header - NOT_RUNNING_APPS="${NOT_RUNNING_APPS} freeswitch" - else - RUNNING_APPS="${RUNNING_APPS} freeswitch" - fi - fi - - if ! ps aux | grep -v grep | grep '[/]usr/sbin/nginx' > /dev/null; then - print_header - NOT_RUNNING_APPS="${NOT_RUNNING_APPS} Nginx" - else - RUNNING_APPS="${RUNNING_APPS} Nginx" - fi - - if ! netstat -ant | grep '8090' > /dev/null; then - print_header - NOT_RUNNING_APPS="${NOT_RUNNING_APPS} ${TOMCAT_SERVICE} or grails" - else - if ps aux | ps -aef | grep -v grep | grep grails | grep run-app > /dev/null; then - print_header - RUNNING_APPS="${RUNNING_APPS} Grails" - echo "# ${TOMCAT_SERVICE}: noticed you are running grails run-app instead of ${TOMCAT_SERVICE}" - else - RUNNING_APPS="${RUNNING_APPS} ${TOMCAT_SERVICE}" - fi - fi - - if ! netstat -ant | grep '8100' > /dev/null; then - print_header - NOT_RUNNING_APPS="${NOT_RUNNING_APPS} LibreOffice" - else - RUNNING_APPS="${RUNNING_APPS} LibreOffice" - fi - - if ! ps aux | grep -v grep | grep '[/]usr/[s]*bin/redis-server' > /dev/null; then - print_header - NOT_RUNNING_APPS="${NOT_RUNNING_APPS} redis-server" + if [ "$NOT_RUNNING_APPS" != "" ]; then + echo "# Not running: ${NOT_RUNNING_APPS}" + fi + + + # + # Check if running development environment + # + if ! grep /var/www/bigbluebutton /etc/bigbluebutton/nginx/client.nginx > /dev/null; then + NEW_LOCATION=$(cat /etc/bigbluebutton/nginx/client.nginx | grep -v '#' | sed -n '/root/{s/[ \t]*root[ \t]*//;s/;//;p}' | head -n 1) + echo "# Warning: nginx is not serving the client from /var/www/bigbluebutton/." + echo "# Instead, it's being served from" + echo "#" + echo "# $NEW_LOCATION" + echo "#" + echo "# (This is OK if you have setup a development environment.) " + echo + + if [ ! -d $NEW_LOCATION ]; then + echo "# Warning: The directory" + echo "#" + echo "# $NEW_LOCATION" + echo "#" + echo "# does not exist. The BigBlueButton client will not load." + echo + fi + fi + + # + # Check if running development environment + # + if ! grep 8090 /etc/bigbluebutton/nginx/web.nginx > /dev/null; then + echo "# Warning: nginx is not serving BigBlueButton's web application" + echo "# from port 8090" + echo "#" + echo "# (This is OK if you have setup a development environment.) " + echo + fi + + + # + # Check red5 applications + # + AVAIL_RED5_APPS="" + UNAVAIL_RED5_APPS="" + DIRECTORIES="bigbluebutton sip video screenshare video-broadcast" + for dir in $DIRECTORIES ; do + if [ ! -d $RED5_DIR/webapps/$dir ]; then + UNAVAIL_RED5_APPS="${UNAVAIL_RED5_APPS} $dir" else - RUNNING_APPS="${RUNNING_APPS} redis-server" + AVAIL_RED5_APPS="${AVAIL_RED5_APPS} $dir" fi + done + + if [ "$UNAVAIL_RED5_APPS" != "" ]; then + echo "# Unavailable red5 apps ($RED5_DIR/webapps/): ${UNAVAIL_RED5_APPS}" + fi + + # + # Checking red5 apps log + # - if [ "$NOT_RUNNING_APPS" != "" ]; then - echo "# Not running: ${NOT_RUNNING_APPS}" - fi - - - # - # Check if running development environment - # - if ! grep /var/www/bigbluebutton /etc/bigbluebutton/nginx/client.nginx > /dev/null; then - NEW_LOCATION=$(cat /etc/bigbluebutton/nginx/client.nginx | grep -v '#' | sed -n '/root/{s/[ \t]*root[ \t]*//;s/;//;p}' | head -n 1) - echo "# Warning: nginx is not serving the client from /var/www/bigbluebutton/." - echo "# Instead, it's being served from" - echo "#" - echo "# $NEW_LOCATION" - echo "#" - echo "# (This is OK if you have setup a development environment.) " - echo - - if [ ! -d $NEW_LOCATION ]; then - echo "# Warning: The directory" - echo "#" - echo "# $NEW_LOCATION" - echo "#" - echo "# does not exist. The BigBlueButton client will not load." - echo - fi - fi - - # - # Check if running development environment - # - if ! grep 8090 /etc/bigbluebutton/nginx/web.nginx > /dev/null; then - echo "# Warning: nginx is not serving BigBlueButton's web application" - echo "# from port 8090" - echo "#" - echo "# (This is OK if you have setup a development environment.) " - echo - fi - - - # - # Check red5 applications - # - AVAIL_RED5_APPS="" - UNAVAIL_RED5_APPS="" - DIRECTORIES="bigbluebutton sip video screenshare" - for dir in $DIRECTORIES ; do - if [ ! -d $RED5_DIR/webapps/$dir ]; then - UNAVAIL_RED5_APPS="${UNAVAIL_RED5_APPS} $dir" - else - AVAIL_RED5_APPS="${AVAIL_RED5_APPS} $dir" - fi - done - - if [ "$UNAVAIL_RED5_APPS" != "" ]; then - echo "# Unavailable red5 apps ($RED5_DIR/webapps/): ${UNAVAIL_RED5_APPS}" - fi - - - # - # Checking red5 apps log - # - - # Give the files a chance to be created (in case we got started with a clean) - COUNT=0 - while [ $COUNT -lt 20 ]; do - let COUNT=COUNT+1 - if [ -f $RED5_DIR/log/sip.log ] && [ -f $RED5_DIR/log/bigbluebutton.log ] && [ -f $RED5_DIR/log/red5.log ] && [ -f $RED5_DIR/log/video.log ] && [ -f $RED5_DIR/log/screenshare-slf.log ]; then + # Give the files a chance to be created (in case we got started with a clean) + COUNT=0 + while [ $COUNT -lt 20 ]; do + let COUNT=COUNT+1 + if [ -f $RED5_DIR/log/sip.log ] && [ -f $RED5_DIR/log/bigbluebutton.log ] && [ -f $RED5_DIR/log/red5.log ] && [ -f $RED5_DIR/log/video.log ] && [ -f $RED5_DIR/log/screenshare-slf.log ]; then let COUNT=20 - else + else echo -n "." sleep 1 - fi - done + fi + done + + RED5_LOG_FILES="bigbluebutton red5 sip video screenshare-slf" + AVAIL_RED5_LOG="" + UNAVAIL_RED5_LOG="" + for file in $RED5_LOG_FILES ; do + if [ ! -f $RED5_DIR/log/$file.log ]; then + UNAVAIL_RED5_LOG="${UNAVAIL_RED5_LOG} $file.log" + else + AVAIL_RED5_LOG="${AVAIL_RED5_LOG} $file.log" + fi + done - RED5_LOG_FILES="bigbluebutton red5 sip video screenshare-slf" - AVAIL_RED5_LOG="" - UNAVAIL_RED5_LOG="" - for file in $RED5_LOG_FILES ; do - if [ ! -f $RED5_DIR/log/$file.log ]; then - UNAVAIL_RED5_LOG="${UNAVAIL_RED5_LOG} $file.log" - else - AVAIL_RED5_LOG="${AVAIL_RED5_LOG} $file.log" - fi - done - - if [ "$UNAVAIL_RED5_LOG" != "" ]; then - echo "# Error: Red5 log files not found" - echo - echo "# Unavailable red5 logs ($RED5_DIR/log): $UNAVAIL_RED5_LOG" - echo - fi - - # - # Check FreeSWITCH - # - - if grep -q "Thread ended for mod_event_socket" /opt/freeswitch/var/log/freeswitch/freeswitch.log; then - echo - echo "# Error: Found text in freeswitch.log:" - echo "#" - echo "# Thread ended for mod_event_socket" - echo "#" - echo "# FreeSWITCH may not be responding to requests on port 8021 (event socket layer)" - echo "# and users may have errors joining audio." - echo "#" - fi - - # - # Check FreeSWITCH - # - - if ! echo "/quit" | /opt/freeswitch/bin/fs_cli - > /dev/null 2>&1; then - echo - echo "#" - echo "# Error: Unable to connect to the FreeSWITCH Event Socket Layer on port 8021" - echo "#" - fi - - - # - # Check for required external commands - # - COMMANDS="ruby gem pdftocairo pdf2swf" - for cmd in $COMMANDS ; do - if ! which $cmd > /dev/null 2>&1; then - echo "# $cmd command not found" - fi - done - - # - # Check if ffmpeg is installed, and whether it is a supported version - # - FFMPEG_VERSION=$(ffmpeg -version 2>/dev/null | grep ffmpeg | cut -d ' ' -f3 | sed 's/--.*//g' | tr -d '\n') - case "$FFMPEG_VERSION" in + if [ "$UNAVAIL_RED5_LOG" != "" ]; then + echo "# Error: Red5 log files not found" + echo + echo "# Unavailable red5 logs ($RED5_DIR/log): $UNAVAIL_RED5_LOG" + echo + fi + + # + # Check FreeSWITCH + # + if grep -q "Thread ended for mod_event_socket" /opt/freeswitch/var/log/freeswitch/freeswitch.log; then + echo + echo "# Error: Found text in freeswitch.log:" + echo "#" + echo "# Thread ended for mod_event_socket" + echo "#" + echo "# FreeSWITCH may not be responding to requests on port 8021 (event socket layer)" + echo "# and users may have errors joining audio." + echo "#" + fi + + # + # Check FreeSWITCH + # + + if ! echo "/quit" | /opt/freeswitch/bin/fs_cli - > /dev/null 2>&1; then + echo + echo "#" + echo "# Error: Unable to connect to the FreeSWITCH Event Socket Layer on port 8021" + echo "#" + fi + + # + # Check for required external commands + # + COMMANDS="ruby gem pdftocairo pdf2swf" + for cmd in $COMMANDS ; do + if ! which $cmd > /dev/null 2>&1; then + echo "# $cmd command not found" + fi + done + + # + # Check if ffmpeg is installed, and whether it is a supported version + # + FFMPEG_VERSION=$(ffmpeg -version 2>/dev/null | grep ffmpeg | cut -d ' ' -f3 | sed 's/--.*//g' | tr -d '\n') + case "$FFMPEG_VERSION" in 4.*.*) - # This is the current supported version; OK. - ;; + # This is the current supported version; OK. + ;; '') - echo "# Warning: No ffmpeg version was found on the system" - echo "# Recording processing will not function" - echo - ;; + echo "# Warning: No ffmpeg version was found on the system" + echo "# Recording processing will not function" + echo + ;; *) - echo "# Warning: The installed ffmpeg version '${FFMPEG_VERSION}' is not recommended." - echo "# Recommend you update to the 4.0.x version of ffmpeg. To upgrade, do the following" - echo "#" - echo "# $SUDO apt-get install software-properties-common" - echo "# $SUDO add-apt-repository ppa:jonathonf/ffmpeg-4" - echo "# $SUDO apt-get update" - echo "# $SUDO apt-get dist-upgrade" - echo "#" - echo - ;; - esac - - - if [ -f /usr/share/red5/log/sip.log ]; then - # - # Checking if voice app registered successfully - # - if cat /usr/share/red5/log/sip.log | grep -q "Failed to register with Sip Server"; then - echo "# Warning: The voice application may not have registered with the sip server." - echo "# Try running: " - echo "#" - echo "# $SUDO bbb-conf --clean" - echo "#" - echo - fi - - # - # check if sip.log has warnings where the user is not registered. - # - - if cat /usr/share/red5/log/sip.log | tail -n1 | grep -q "Call request for default but not registered"; then - echo "# Error: The voice app is not registered with SIP server. Audio might not be working correctly." - echo - fi - else - echo "# Error: The following log file was not found" - echo "# /usr/share/red5/log/sip.log " - echo + echo "# Warning: The installed ffmpeg version '${FFMPEG_VERSION}' is not recommended." + echo "# Recommend you update to the 4.0.x version of ffmpeg. To upgrade, do the following" + echo "#" + echo "# $SUDO apt-get install software-properties-common" + echo "# $SUDO add-apt-repository ppa:jonathonf/ffmpeg-4" + echo "# $SUDO apt-get update" + echo "# $SUDO apt-get dist-upgrade" + echo "#" + echo + ;; + esac + + + if [[ -f /usr/share/red5/log/sip.log ]]; then + # + # Checking if voice app registered successfully + # + if cat /usr/share/red5/log/sip.log | grep -q "Failed to register with Sip Server"; then + echo "# Warning: The voice application may not have registered with the sip server." + echo "# Try running: " + echo "#" + echo "# $SUDO bbb-conf --clean" + echo "#" + echo + fi + + # + # check if sip.log has warnings where the user is not registered. + # + + if cat /usr/share/red5/log/sip.log | tail -n1 | grep -q "Call request for default but not registered"; then + echo "# Error: The voice app is not registered with SIP server. Audio might not be working correctly." + echo + fi + else + echo "# Error: The following log file was not found" + echo "# /usr/share/red5/log/sip.log " + echo + fi + + # + # Check that the servlet container has started properly and has created log files + # + if [ -z "$(ls -A $SERVLET_LOGS)" ]; then + echo "# empty directory: $SERVLET_LOGS contains no logs" + fi + + # + # Check that bigbluebutton in red5 has started properly (less than 100 lines indicates that it + # didn't start) + # + if [ -f $RED5_DIR/log/bigbluebutton.log ]; then + BBB_RED5_LOG=$(stat -c%s $RED5_DIR/log/bigbluebutton.log) + if [ $BBB_RED5_LOG -lt 100 ]; then + echo "# Log file too short (less than 100 lines): $RED5_DIR/log/bigbluebutton.log (red5)" fi + else + echo "# No $RED5_DIR/log/bigbluebutton.log" + fi + + # + # Check if the user is running their own bbb-web + # + if grep -q 8888 /etc/bigbluebutton/nginx/web.nginx; then + if ! netstat -ant | grep '8888' > /dev/null; then + echo "# Warning: There is no application server listening to port 8888." + echo + fi + fi + + + # + # Check if the local server can access the API. This is a common problem when setting up BigBlueButton behind + # a firewall + # + BBB_WEB=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') + check_no_value server_name /etc/nginx/sites-available/bigbluebutton $BBB_WEB - # - # Check that the servlet container has started properly and has created log files - # - if [ -z "$(ls -A $SERVLET_LOGS)" ]; then - echo "# empty directory: $SERVLET_LOGS contains no logs" - fi - - # - # Check that bigbluebutton in red5 has started properly (less than 100 lines indicates that it - # didn't start) - # - if [ -f $RED5_DIR/log/bigbluebutton.log ]; then - BBB_RED5_LOG=$(stat -c%s $RED5_DIR/log/bigbluebutton.log) - if [ $BBB_RED5_LOG -lt 100 ]; then - echo "# Log file too short (less than 100 lines): $RED5_DIR/log/bigbluebutton.log (red5)" - fi - else - echo "# No $RED5_DIR/log/bigbluebutton.log" - fi - - # - # Check if the user is running their own bbb-web - # - if grep -q 8888 /etc/bigbluebutton/nginx/web.nginx; then - if ! netstat -ant | grep '8888' > /dev/null; then - echo "# Warning: There is no application server listening to port 8888." - echo - fi - fi - - - # - # Check if the local server can access the API. This is a common problem when setting up BigBlueButton behind - # a firewall - # - BBB_WEB=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') - check_no_value server_name /etc/nginx/sites-available/bigbluebutton $BBB_WEB - - COUNT=0 - while [ $COUNT -lt 80 ]; do - let COUNT=COUNT+1 - timeout 1s wget $PROTOCOL://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS - if [ $? -eq 0 ]; then + COUNT=0 + while [ $COUNT -lt 80 ]; do + let COUNT=COUNT+1 + timeout 1s wget $PROTOCOL://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS + if [ $? -eq 0 ]; then let COUNT=80 - else + else echo -n "." sleep 1 - fi - done - echo + fi + done + echo + + if ! wget $PROTOCOL://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then + echo "# Error: Could not connect to the configured hostname/IP address" + echo "#" + echo "# $PROTOCOL://$BBB_WEB/" + echo "#" + echo "# If your BigBlueButton server is behind a firewall, see FAQ." + echo + fi - if ! wget $PROTOCOL://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then - echo "# Error: Could not connect to the configured hostname/IP address" - echo "#" - echo "# $PROTOCOL://$BBB_WEB/" + # + # Check that BigBlueButton can connect to port 1935 + # + if [[ ! -z $NGINX_IP && $DISTRIB_ID != "centos" ]]; then + if ! nc -w 3 $NGINX_IP 1935 > /dev/null; then + echo "# Error: Unable to connect to port 1935 (RTMP) on $NGINX_IP" + echo + fi + fi + + + BBB_SIP_APP_IP=$(cat /usr/share/red5/webapps/sip/WEB-INF/bigbluebutton-sip.properties | grep -v '#' | sed -n '/^bbb.sip.app.ip=/{s/.*=//;s/;//;p}') + if [ $BBB_SIP_APP_IP != "127.0.0.1" ]; then + if [ "$BBB_SIP_APP_IP" != $IP ]; then + echo "# Warning: The setting of $BBB_SIP_APP_IP for bbb.sip.app.ip in" + echo "#" + echo "# /usr/share/red5/webapps/sip/WEB-INF/bigbluebutton-sip.properties" + echo "#" + echo "# does not match the local IP address ($IP)." + echo "# (This is OK if you've manually changed the values to an external " + echo "# FreeSWITCH server.)" + echo + fi + + SIP_IP=$(netstat -ant | grep 5060 | head -n1 | awk -F" " '{print $4}' | cut -d: -f1) + if [ -z $SIP_IP ]; then + echo "# Error: Could not detect FreeSWITCH listening on port 5060" + echo + else + if [ "$BBB_SIP_APP_IP" != $SIP_IP ]; then + echo "# Error: FreeSWITCH is listening on IP address $SIP_IP for SIP calls, but " + echo "# The IP address ($BBB_SIP_APP_IP) set bbb.sip.app.ip." echo "#" - echo "# If your BigBlueButton server is behind a firewall, see FAQ." echo + fi fi + fi - # - # Check that BigBlueButton can connect to port 1935 - # - if [[ ! -z $NGINX_IP && $DISTRIB_ID != "centos" ]]; then - if ! nc -w 3 $NGINX_IP 1935 > /dev/null; then - echo "# Error: Unable to connect to port 1935 (RTMP) on $NGINX_IP" - echo - fi - fi - - - BBB_SIP_APP_IP=$(cat /usr/share/red5/webapps/sip/WEB-INF/bigbluebutton-sip.properties | grep -v '#' | sed -n '/^bbb.sip.app.ip=/{s/.*=//;s/;//;p}') - if [ $BBB_SIP_APP_IP != "127.0.0.1" ]; then - if [ "$BBB_SIP_APP_IP" != $IP ]; then - echo "# Warning: The setting of $BBB_SIP_APP_IP for bbb.sip.app.ip in" - echo "#" - echo "# /usr/share/red5/webapps/sip/WEB-INF/bigbluebutton-sip.properties" - echo "#" - echo "# does not match the local IP address ($IP)." - echo "# (This is OK if you've manually changed the values to an external " - echo "# FreeSWITCH server.)" - echo - fi - - SIP_IP=$(netstat -ant | grep 5060 | head -n1 | awk -F" " '{print $4}' | cut -d: -f1) - if [ -z $SIP_IP ]; then - echo "# Error: Could not detect FreeSWITCH listening on port 5060" - echo - else - if [ "$BBB_SIP_APP_IP" != $SIP_IP ]; then - echo "# Error: FreeSWITCH is listening on IP address $SIP_IP for SIP calls, but " - echo "# The IP address ($BBB_SIP_APP_IP) set bbb.sip.app.ip." - echo "#" - echo - fi - fi - fi - - if [ "$SIP_NGINX_IP" != $IP ]; then - if [ "$SIP_NGINX_IP" != "\$freeswitch_addr" ]; then - echo "# Warning: The setting of $SIP_NGINX_IP for proxy_pass in" - echo "#" - echo "# /etc/bigbluebutton/nginx/sip.nginx" - echo "#" - echo "# does not match the local IP address ($IP)." - echo "# (This is OK if you've manually changed the values)" - echo - fi + if [ "$SIP_NGINX_IP" != $IP ]; then + if [ "$SIP_NGINX_IP" != "\$freeswitch_addr" ]; then + echo "# Warning: The setting of $SIP_NGINX_IP for proxy_pass in" + echo "#" + echo "# /etc/bigbluebutton/nginx/sip.nginx" + echo "#" + echo "# does not match the local IP address ($IP)." + echo "# (This is OK if you've manually changed the values)" + echo fi + fi - VARS_IP=$(cat $FREESWITCH_VARS | sed -n '/"local_ip_v4/{s/.*local_ip_v4=//;s/".*//;p}') - if [[ "$VARS_IP" != "127.0.0.1" ]] && [[ "$VARS_IP" != "auto" ]]; then - if [ "$VARS_IP" != $IP ]; then - echo "# Warning: The setting of $VARS_IP for local_ip_v4 in" - echo "#" - echo "# $FREESWITCH_VARS" - echo "#" - echo "# does not match the local IP address ($IP)." - echo "# (This is OK if you've manually changed the values)" - echo - fi + VARS_IP=$(cat $FREESWITCH_VARS | sed -n '/"local_ip_v4/{s/.*local_ip_v4=//;s/".*//;p}') + if [[ "$VARS_IP" != "127.0.0.1" ]] && [[ "$VARS_IP" != "auto" ]]; then + if [ "$VARS_IP" != $IP ]; then + echo "# Warning: The setting of $VARS_IP for local_ip_v4 in" + echo "#" + echo "# $FREESWITCH_VARS" + echo "#" + echo "# does not match the local IP address ($IP)." + echo "# (This is OK if you've manually changed the values)" + echo fi + fi - if [ -d ${LTI_DIR} ]; then - if test ${TOMCAT_DIR}/webapps/lti.war -nt ${LTI_DIR}; then - echo "# Error: The updated lti.war did not deploy. To manually deploy:" - echo "#" - echo "# $SUDO touch ${SERVLET_DIR}/lti.war" - echo "#" - echo - fi - fi - - if [ -d ${TOMCAT_DIR}/webapps/demo ]; then - if test ${TOMCAT_DIR}/webapps/demo.war -nt ${TOMCAT_DIR}/webapps/demo; then - echo "# Error: The updated demo.war did not deploy. To manually deploy:" - echo "#" - echo "# $SUDO touch ${SERVLET_DIR}/demo.war" - echo "#" - echo - fi - fi - - if (( $MEM < 3940 )); then - echo "# Warning: You are running BigBlueButton on a server with less than 4G of memory. Your" - echo "# performance may suffer." - echo - fi + if [ -d ${LTI_DIR} ]; then + if test ${TOMCAT_DIR}/webapps/lti.war -nt ${LTI_DIR}; then + echo "# Error: The updated lti.war did not deploy. To manually deploy:" + echo "#" + echo "# $SUDO touch ${SERVLET_DIR}/lti.war" + echo "#" + echo + fi + fi - if [ -f ${TOMCAT_DIR}/webapps/demo/demo1.jsp ]; then - BBB_WEB_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') - echo "# Warning: The API demos are installed and accessible from:" - echo "#" - echo "# $BBB_WEB_URL" - echo "#" - echo "# and" - echo "#" - echo "# $BBB_WEB_URL/demo/demo1.jsp" - echo "#" - echo "# These API demos allow anyone to access your server without authentication" - echo "# to create/manage meetings and recordings. They are for testing purposes only." - echo "# If you are running a production system, remove them by running:" - echo "#" - echo "# $SUDO apt-get purge bbb-demo" - echo - fi - - if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then - CHECK_HOST=$(cat /var/www/bigbluebutton/check/conf/config.xml | grep "<uri>rtmp" | head -1 | sed 's/.*rtmp[s]*:\/\///g' | sed 's/\/.*//g' | tr -d '\015') - BBB_WEB_URL=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') - echo "# Warning: The client self check is installed and accessible from:" - echo "#" - echo "# $BBB_WEB_URL/check" - echo "#" - echo - - fi - - if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then - LTI_URL='http://'$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiEndPoint/{s/^.*=//;p}')'/lti/tool' - echo "# Warning: The IMS Learning Tools Integration (LTI) is accessible from:" - echo "#" - echo "# $LTI_URL" - echo "#" - echo "# To get the access parameters for LTI, run the command" - echo "#" - echo "# bbb-conf --lti" - echo - fi - - BBB_WEB=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') - DEFAULT_PDF=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^beans.presentationService.defaultUploadedPresentation/{s/.*=//;p}') - if echo $DEFAULT_PDF | grep -q "bigbluebutton.web.serverURL"; then - if ! echo "$BBB_WEB$(echo $DEFAULT_PDF | sed 's/${bigbluebutton.web.serverURL}//g')" | xargs wget -q -O /dev/null; then - echo "# Error: Unable to reach default URL for presentation:" - echo "#" - echo "# $BBB_WEB$(echo $DEFAULT_PDF | sed 's/${bigbluebutton.web.serverURL}//g')" - echo "#" - echo "# Check value for beans.presentationService.defaultUploadedPresentation in" - echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties" - fi - else - if ! echo "$DEFAULT_PDF" | xargs wget -q -O /dev/null; then - echo "# Error: Unable to reach default URL for presentation" - echo "#" - echo "# $DEFAULT_PDF" - echo "#" - echo "# Check value for beans.presentationService.defaultUploadedPresentation in" - echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties" - fi - fi - - if [ -f /var/run/red5.pid ]; then - RED5_PID_PS=$(ps -u red5 | grep java | sed 's/^[ ]*//g' | cut -d" " -f1) - RED5_PID_FILE=$(cat /var/run/red5.pid) - if [[ "$RED5_PID_PS" != "$RED5_PID_FILE" ]]; then + if [ -d ${TOMCAT_DIR}/webapps/demo ]; then + if test ${TOMCAT_DIR}/webapps/demo.war -nt ${TOMCAT_DIR}/webapps/demo; then + echo "# Error: The updated demo.war did not deploy. To manually deploy:" + echo "#" + echo "# $SUDO touch ${SERVLET_DIR}/demo.war" + echo "#" + echo + fi + fi + + if (( $MEM < 3940 )); then + echo "# Warning: You are running BigBlueButton on a server with less than 4G of memory. Your" + echo "# performance may suffer." + echo + fi + + if [ -f ${TOMCAT_DIR}/webapps/demo/demo1.jsp ]; then + BBB_WEB_URL=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') + echo "# Warning: The API demos are installed and accessible from:" + echo "#" + echo "# $BBB_WEB_URL" + echo "#" + echo "# and" + echo "#" + echo "# $BBB_WEB_URL/demo/demo1.jsp" + echo "#" + echo "# These API demos allow anyone to access your server without authentication" + echo "# to create/manage meetings and recordings. They are for testing purposes only." + echo "# If you are running a production system, remove them by running:" + echo "#" + echo "# $SUDO apt-get purge bbb-demo" + echo + fi + + if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then + CHECK_HOST=$(cat /var/www/bigbluebutton/check/conf/config.xml | grep "<uri>rtmp" | head -1 | sed 's/.*rtmp[s]*:\/\///g' | sed 's/\/.*//g' | tr -d '\015') + BBB_WEB_URL=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') + echo "# Warning: The client self check is installed and accessible from:" + echo "#" + echo "# $BBB_WEB_URL/check" + echo "#" + echo + fi + + if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then + LTI_URL='http://'$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^ltiEndPoint/{s/^.*=//;p}')'/lti/tool' + echo "# Warning: The IMS Learning Tools Integration (LTI) is accessible from:" + echo "#" + echo "# $LTI_URL" + echo "#" + echo "# To get the access parameters for LTI, run the command" + echo "#" + echo "# bbb-conf --lti" + echo + fi + + BBB_WEB=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}') + DEFAULT_PDF=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^beans.presentationService.defaultUploadedPresentation/{s/.*=//;p}') + if echo $DEFAULT_PDF | grep -q "bigbluebutton.web.serverURL"; then + if ! echo "$BBB_WEB$(echo $DEFAULT_PDF | sed 's/${bigbluebutton.web.serverURL}//g')" | xargs wget -q -O /dev/null; then + echo "# Error: Unable to reach default URL for presentation:" + echo "#" + echo "# $BBB_WEB$(echo $DEFAULT_PDF | sed 's/${bigbluebutton.web.serverURL}//g')" + echo "#" + echo "# Check value for beans.presentationService.defaultUploadedPresentation in" + echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties" + fi + else + if ! echo "$DEFAULT_PDF" | xargs wget -q -O /dev/null; then + echo "# Error: Unable to reach default URL for presentation" + echo "#" + echo "# $DEFAULT_PDF" + echo "#" + echo "# Check value for beans.presentationService.defaultUploadedPresentation in" + echo "# ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties" + fi + fi + + if [ -f /var/run/red5.pid ]; then + RED5_PID_PS=$(ps -u red5 | grep java | sed 's/^[ ]*//g' | cut -d" " -f1) + RED5_PID_FILE=$(cat /var/run/red5.pid) + if [[ "$RED5_PID_PS" != "$RED5_PID_FILE" ]]; then if [ "$(ps -aef | grep "su -p -s /bin/sh red5 " | wc --lines)" == "2" ]; then RED5_PID_PS=$(ps -aef | grep "su -p -s /bin/sh red5 " | head -n 1 | awk '{print $2}') if [[ "$RED5_PID_PS" != "$RED5_PID_FILE" ]]; then - echo "# Error: red5 process ID does not match value in PID file" - echo "#" - echo "# pid from top: $RED5_PID_PS" - echo "# /var/run/red5.pid: $RED5_PID_FILE" - echo "#" + echo "# Error: red5 process ID does not match value in PID file" + echo "#" + echo "# pid from top: $RED5_PID_PS" + echo "# /var/run/red5.pid: $RED5_PID_FILE" + echo "#" fi else echo "# Error: red5 process ID does not match value in PID file" echo "#" echo "# pid from top: $RED5_PID_PS" - echo "# /var/run/red5.pid: $RED5_PID_FILE" + echo "# /var/run/red5.pid: $RED5_PID_FILE" echo "#" fi - fi fi + fi - if [ "$(cat /usr/share/bbb-apps-akka/conf/application.conf | sed -n '/sharedSecret.*/{s/[^"]*"//;s/".*//;p}')" == "changeme" ]; then - BBB_WEB_IP=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') - echo "# Error: Detected that /usr/share/bbb-apps-akka/conf/application.conf has the default" - echo "# configuration values. To update, run" - echo "#" - echo "# $SUDO bbb-conf --setip $BBB_WEB_IP" - echo "#" - fi + if [ "$(cat /usr/share/bbb-apps-akka/conf/application.conf | sed -n '/sharedSecret.*/{s/[^"]*"//;s/".*//;p}')" == "changeme" ]; then + BBB_WEB_IP=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') + echo "# Error: Detected that /usr/share/bbb-apps-akka/conf/application.conf has the default" + echo "# configuration values. To update, run" + echo "#" + echo "# $SUDO bbb-conf --setip $BBB_WEB_IP" + echo "#" + fi - if [ -z $(cat /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties | tr -d '\r' | sed -n "/useH264[ ]*=[ ]*/{s/useH264[ ]*=[ ]*//;p}") ]; then - echo "# Error: Detected there is no value set for useH264 in" - echo "#" - echo "# /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties" - echo "#" - fi + if [ -z $(cat /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties | tr -d '\r' | sed -n "/useH264[ ]*=[ ]*/{s/useH264[ ]*=[ ]*//;p}") ]; then + echo "# Error: Detected there is no value set for useH264 in" + echo "#" + echo "# /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties" + echo "#" + fi - if bbb-conf --status | grep -q inactive; then - echo "# Error: Detected some processes have not started correctly" - echo "#" - echo "# $(bbb-conf --status | grep inactive)" - echo "#" - fi + if bbb-conf --status | grep -q inactive; then + echo "# Error: Detected some processes have not started correctly" + echo "#" + echo "# $(bbb-conf --status | grep inactive)" + echo "#" + fi - if systemctl status freeswitch | grep -q SETSCHEDULER; then - echo "# Error: FreeSWITCH failed to start with SETSCHEDULER error, see" - echo "#" - echo "# http://docs.bigbluebutton.org/install/install.html#freeswitch-fails-to-start-with-a-setscheduler-error" - echo "#" - fi + if systemctl status freeswitch | grep -q SETSCHEDULER; then + echo "# Error: FreeSWITCH failed to start with SETSCHEDULER error, see" + echo "#" + echo "# http://docs.bigbluebutton.org/install/install.html#freeswitch-fails-to-start-with-a-setscheduler-error" + echo "#" + fi + + NCPU=$(nproc --all) + if [ "$NCPU" -lt "4" ]; then + echo "# Warning: found only $NCPU cores, whereas this sherver should have (at least) 4 CPU cores" + echo "# to run BigBlueButton in production." + echo "#" + echo "# http://docs.bigbluebutton.org/install/install.html#minimum-server-requirements" + echo "#" + fi + + if [ "$PROTOCOL" == "https" ]; then + if ! cat $SIP_CONFIG | grep -v '#' | grep proxy_pass | head -n 1 | grep -q https; then + echo "# Warning: You have this server defined for https, but in" + echo "#" + echo "# $SIP_CONFIG" + echo "#" + echo "# did not find the use of https in definition for proxy_pass" + echo "#" + echo "# $(cat $SIP_CONFIG | grep -v '#' | grep proxy_pass | head -n 1)" + echo "#" + fi + + if ! cat $SIP_CONFIG | grep -v '#' | grep proxy_pass | head -n 1 | grep -q 7443; then + echo + echo "# Warning: You have this server defined for https, but in" + echo "#" + echo "# $SIP_CONFIG" + echo "#" + echo "# did not find the use of port 7443 in definition for proxy_pass" + echo "#" + echo "# $(cat $SIP_CONFIG | grep -v '#' | grep proxy_pass | head -n 1)" + echo "#" + fi + fi - NCPU=`nproc --all` - if [ "$NCPU" -lt "4" ]; then - echo "# Warning: found only $NCPU cores, whereas this sherver should have (at least) 4 CPU cores" - echo "# to run BigBlueButton in production." - echo "#" - echo "# http://docs.bigbluebutton.org/install/install.html#minimum-server-requirements" - echo "#" - fi - exit 0 + exit 0 } @@ -1603,544 +1425,527 @@ check_state() { # Print out the status of the current setup and look for configuration issues # if [ $CHECK ]; then - need_root - - echo - if [ $DISTRIB_ID == "centos" ]; then - echo "BigBlueButton Server $BIGBLUEBUTTON_RELEASE ($(rpm -qa | grep bbb | grep -v bbb-demo | grep -v bbb-lti | grep -v bbb-redis | grep -v bbb-tomcat | grep -v freeswitch | sed 's/.*[0-9].[0-9].[0-9]-//g' | sed 's/\..*//g' | awk '{ sum+=$1} END {print sum}'))" - else - echo "BigBlueButton Server $BIGBLUEBUTTON_RELEASE ($(dpkg -l | grep bbb | grep -v bbb-demo | grep -v bbb-lti | sed -n '/[0-9].[0-9].[0-9]-/{s/.*[0-9].[0-9].[0-9]-//;s/;//;p}' | awk '{ sum+=$1} END {print sum}'))" - fi - - echo " Kernel version:" `uname -r` - - if [ $DISTRIB_ID == "centos" ]; then - echo -n " Distribution: $(cat /etc/redhat-release)" - else - source /etc/lsb-release; - echo -n " Distribution: $DISTRIB_DESCRIPTION " - fi + need_root - if [ `uname -m` == "x86_64" ]; then - echo "(64-bit)" - elif [ `uname -m` == "i686" ]; then - echo "(32-bit)" - fi - - echo " Memory: $MEM MB" - echo " CPU cores: $NCPU" - - echo - echo "$BBB_WEB_CONFIG (bbb-web)" - echo " bigbluebutton.web.serverURL: $(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}')" - echo " defaultGuestPolicy: $(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^defaultGuestPolicy/{s/.*=//;p}')" - echo " svgImagesRequired: $(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^svgImagesRequired/{s/.*=//;p}')" - - echo - echo "/etc/nginx/sites-available/bigbluebutton (nginx)" - echo " server name: $NGINX_IP" - - PORT=$(cat /etc/nginx/sites-available/bigbluebutton | grep -v '#' | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}' | grep -v ssl | tr --delete '\n' | sed 's/\[/, \[/g' | sed 's/0$/0\n/g') - echo " port: $PORT" - if cat /etc/nginx/sites-available/bigbluebutton | grep -v '#' | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}' | grep ssl > /dev/null; then - echo " port: 443 ssl" - fi - BBB_CLIENT_DOC_ROOT=$(cat /etc/bigbluebutton/nginx/client.nginx | grep -v '#' | grep \/client -A 1 | head -n 2 | grep root | sed -n '{s/[ \t]*root[ ]*//;s/;//;p}') - echo " bbb-client dir: $BBB_CLIENT_DOC_ROOT" - - echo - echo "/var/www/bigbluebutton/client/conf/config.xml (bbb-client)" - PORT_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/porttest /{s/.*host="//;s/".*//;p}') - echo " Port test (tunnel): $PORT_IP" - - RED5_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/uri.*video/{s/.*rtmp[s]*:\/\///;s/\/.*//;p}') - WEBRTC_ENABLED_CLIENT=`xmlstarlet sel -t -m "config/modules/module[@name='PhoneModule']" -v @useWebRTCIfAvailable /var/www/bigbluebutton/client/conf/config.xml` - echo " red5: $RED5_IP" - echo " useWebrtcIfAvailable: $WEBRTC_ENABLED_CLIENT" - - echo - echo "$FREESWITCH_VARS (FreeSWITCH)" - echo " local_ip_v4: $(xmlstarlet sel -t -m '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "local_ip_v4=")]' -v @data $FREESWITCH_VARS | sed 's/local_ip_v4=//g')" - echo " external_rtp_ip: $(xmlstarlet sel -t -m '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "external_rtp_ip=")]' -v @data $FREESWITCH_VARS | sed 's/external_rtp_ip=//g')" - echo " external_sip_ip: $(xmlstarlet sel -t -m '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "external_sip_ip=")]' -v @data $FREESWITCH_VARS | sed 's/external_sip_ip=//g')" - - echo - echo "$FREESWITCH_EXTERNAL (FreeSWITCH)" - echo " ext-rtp-ip: $(xmlstarlet sel -t -m 'profile/settings/param[@name="ext-rtp-ip"]' -v @value $FREESWITCH_EXTERNAL)" - echo " ext-sip-ip: $(xmlstarlet sel -t -m 'profile/settings/param[@name="ext-sip-ip"]' -v @value $FREESWITCH_EXTERNAL)" - echo " ws-binding: $(xmlstarlet sel -t -m 'profile/settings/param[@name="ws-binding"]' -v @value $FREESWITCH_EXTERNAL)" - echo " wss-binding: $(xmlstarlet sel -t -m 'profile/settings/param[@name="wss-binding"]' -v @value $FREESWITCH_EXTERNAL)" - - if [ -f ${SERVLET_DIR}/demo/bbb_api_conf.jsp ]; then - BBB_WEB_URL=$(cat ${SERVLET_DIR}//WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') - echo - echo "${SERVLET_DIR}/demo/bbb_api_conf.jsp (API demos)" - echo " url: $BBB_WEB_URL" - fi - -# if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then -# LTI_URL=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^bigbluebuttonURL/{s/.*http[s]:\/\///;s/\/.*//;p}' | tr -d '\015') -# echo -# echo "${LTI_DIR}/WEB-INF/classes/lti-config.properties (LTI integration)" -# echo " api url: $LTI_URL" -# fi - - if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then - CHECK_URL=$(cat /var/www/bigbluebutton/check/conf/config.xml | grep "<uri>rtmp" | head -1 | sed 's/.*rtmp[s]*:\/\///g' | sed 's/\/.*//g' | tr -d '\015') - echo - echo "/var/www/bigbluebutton/check/conf/config.xml (client check)" - echo " client check: $CHECK_URL" - fi - - if [ -f $RECORD_CONFIG ]; then - echo - echo "$RECORD_CONFIG (record and playback)" - echo " playback_host: $(yq r $RECORD_CONFIG playback_host)" - echo " playback_protocol: $(yq r $RECORD_CONFIG playback_protocol)" - echo " ffmpeg: $(ffmpeg -version 2>/dev/null | grep ffmpeg | cut -d ' ' -f3 | sed 's/--.*//g' | tr -d '\n')" - fi - - if [ -f $SIP_CONFIG ]; then - echo - echo "$SIP_CONFIG (sip.nginx)" - echo " proxy_pass: $SIP_NGINX_IP" - fi + echo + if [ $DISTRIB_ID == "centos" ]; then + echo "BigBlueButton Server $BIGBLUEBUTTON_RELEASE ($(rpm -qa | grep bbb | grep -v bbb-demo | grep -v bbb-lti | grep -v bbb-redis | grep -v bbb-tomcat | grep -v freeswitch | sed 's/.*[0-9].[0-9].[0-9]-//g' | sed 's/\..*//g' | awk '{ sum+=$1} END {print sum}'))" + else + echo "BigBlueButton Server $BIGBLUEBUTTON_RELEASE ($(dpkg -l | grep bbb | grep -v bbb-demo | grep -v bbb-lti | sed -n '/[0-9].[0-9].[0-9]-/{s/.*[0-9].[0-9].[0-9]-//;s/;//;p}' | awk '{ sum+=$1} END {print sum}'))" + fi - if [ -f $KURENTO_CONFIG ]; then - echo - echo "$KURENTO_CONFIG (Kurento SFU)" - echo " kurento.ip: $(yq r $KURENTO_CONFIG kurento[0].ip)" - echo " kurento.url: $(yq r $KURENTO_CONFIG kurento[0].url)" - echo " localIpAddress: $(yq r $KURENTO_CONFIG localIpAddress)" - echo " recordScreenSharing: $(yq r $KURENTO_CONFIG recordScreenSharing)" - echo " recordWebcams: $(yq r $KURENTO_CONFIG recordWebcams)" - echo " codec_video_main: $(yq r $KURENTO_CONFIG conference-media-specs.codec_video_main)" - echo " codec_video_content: $(yq r $KURENTO_CONFIG conference-media-specs.codec_video_content)" - fi + echo " Kernel version:" $(uname -r) - if [ -f $HTML5_CONFIG ]; then - echo - echo "/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml (HTML5 client)" - echo " build: $(yq r $HTML5_CONFIG public.app.html5ClientBuild)" - echo " kurentoUrl: $(yq r $HTML5_CONFIG public.kurento.wsUrl)" - echo " enableListenOnly: $(yq r $HTML5_CONFIG public.kurento.enableListenOnly)" - fi + if [ $DISTRIB_ID == "centos" ]; then + echo -n " Distribution: $(cat /etc/centos-release)" + else + source /etc/lsb-release + echo -n " Distribution: $DISTRIB_DESCRIPTION " + fi + + if [ $(uname -m) == "x86_64" ]; then + echo "(64-bit)" + elif [ $(uname -m) == "i686" ]; then + echo "(32-bit)" + fi + + echo " Memory: $MEM MB" + echo " CPU cores: $NCPU" + + echo + echo "$BBB_WEB_CONFIG (bbb-web)" + echo " bigbluebutton.web.serverURL: $(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}')" + echo " defaultGuestPolicy: $(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^defaultGuestPolicy/{s/.*=//;p}')" + echo " svgImagesRequired: $(cat $BBB_WEB_CONFIG | grep -v '#' | sed -n '/^svgImagesRequired/{s/.*=//;p}')" + + echo + echo "/etc/nginx/sites-available/bigbluebutton (nginx)" + echo " server name: $NGINX_IP" + + PORT=$(cat /etc/nginx/sites-available/bigbluebutton | grep -v '#' | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}' | grep -v ssl | tr --delete '\n' | sed 's/\[/, \[/g' | sed 's/0$/0\n/g') + echo " port: $PORT" + if cat /etc/nginx/sites-available/bigbluebutton | grep -v '#' | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}' | grep ssl > /dev/null; then + echo " port: 443 ssl" + fi + BBB_CLIENT_DOC_ROOT=$(cat /etc/bigbluebutton/nginx/client.nginx | grep -v '#' | grep \/client -A 1 | head -n 2 | grep root | sed -n '{s/[ \t]*root[ ]*//;s/;//;p}') + echo " bbb-client dir: $BBB_CLIENT_DOC_ROOT" + + echo + echo "/var/www/bigbluebutton/client/conf/config.xml (bbb-client)" + PORT_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/porttest /{s/.*host="//;s/".*//;p}') + echo " Port test (tunnel): $PORT_IP" + + RED5_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/uri.*video/{s/.*rtmp[s]*:\/\///;s/\/.*//;p}') + WEBRTC_ENABLED_CLIENT=$(xmlstarlet sel -t -m "config/modules/module[@name='PhoneModule']" -v @useWebRTCIfAvailable /var/www/bigbluebutton/client/conf/config.xml) + echo " red5: $RED5_IP" + echo " useWebrtcIfAvailable: $WEBRTC_ENABLED_CLIENT" + + echo + echo "$FREESWITCH_VARS (FreeSWITCH)" + echo " local_ip_v4: $(xmlstarlet sel -t -m '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "local_ip_v4=")]' -v @data $FREESWITCH_VARS | sed 's/local_ip_v4=//g')" + echo " external_rtp_ip: $(xmlstarlet sel -t -m '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "external_rtp_ip=")]' -v @data $FREESWITCH_VARS | sed 's/external_rtp_ip=//g')" + echo " external_sip_ip: $(xmlstarlet sel -t -m '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "external_sip_ip=")]' -v @data $FREESWITCH_VARS | sed 's/external_sip_ip=//g')" + + echo + echo "$FREESWITCH_EXTERNAL (FreeSWITCH)" + echo " ext-rtp-ip: $(xmlstarlet sel -t -m 'profile/settings/param[@name="ext-rtp-ip"]' -v @value $FREESWITCH_EXTERNAL)" + echo " ext-sip-ip: $(xmlstarlet sel -t -m 'profile/settings/param[@name="ext-sip-ip"]' -v @value $FREESWITCH_EXTERNAL)" + echo " ws-binding: $(xmlstarlet sel -t -m 'profile/settings/param[@name="ws-binding"]' -v @value $FREESWITCH_EXTERNAL)" + echo " wss-binding: $(xmlstarlet sel -t -m 'profile/settings/param[@name="wss-binding"]' -v @value $FREESWITCH_EXTERNAL)" + + if [ -f ${SERVLET_DIR}/demo/bbb_api_conf.jsp ]; then + BBB_WEB_URL=$(cat ${SERVLET_DIR}//WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}') + echo + echo "${SERVLET_DIR}/demo/bbb_api_conf.jsp (API demos)" + echo " url: $BBB_WEB_URL" + fi + +# if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then +# LTI_URL=$(cat ${LTI_DIR}/WEB-INF/classes/lti-config.properties | grep -v '#' | sed -n '/^bigbluebuttonURL/{s/.*http[s]:\/\///;s/\/.*//;p}' | tr -d '\015') +# echo +# echo "${LTI_DIR}/WEB-INF/classes/lti-config.properties (LTI integration)" +# echo " api url: $LTI_URL" +# fi + + if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then + CHECK_URL=$(cat /var/www/bigbluebutton/check/conf/config.xml | grep "<uri>rtmp" | head -1 | sed 's/.*rtmp[s]*:\/\///g' | sed 's/\/.*//g' | tr -d '\015') + echo + echo "/var/www/bigbluebutton/check/conf/config.xml (client check)" + echo " client check: $CHECK_URL" + fi + + if [ -f $RECORD_CONFIG ]; then + echo + echo "$RECORD_CONFIG (record and playback)" + echo " playback_host: $(yq r $RECORD_CONFIG playback_host)" + echo " playback_protocol: $(yq r $RECORD_CONFIG playback_protocol)" + echo " ffmpeg: $(ffmpeg -version 2>/dev/null | grep ffmpeg | cut -d ' ' -f3 | sed 's/--.*//g' | tr -d '\n')" + fi + + if [ -f $SIP_CONFIG ]; then + echo + echo "$SIP_CONFIG (sip.nginx)" + echo " proxy_pass: $SIP_NGINX_IP" + fi + + if [ -f $KURENTO_CONFIG ]; then + echo + echo "$KURENTO_CONFIG (Kurento SFU)" + echo " kurento.ip: $(yq r $KURENTO_CONFIG kurento[0].ip)" + echo " kurento.url: $(yq r $KURENTO_CONFIG kurento[0].url)" + echo " localIpAddress: $(yq r $KURENTO_CONFIG localIpAddress)" + echo " recordScreenSharing: $(yq r $KURENTO_CONFIG recordScreenSharing)" + echo " recordWebcams: $(yq r $KURENTO_CONFIG recordWebcams)" + echo " codec_video_main: $(yq r $KURENTO_CONFIG conference-media-specs.codec_video_main)" + echo " codec_video_content: $(yq r $KURENTO_CONFIG conference-media-specs.codec_video_content)" + fi + if [ -f $HTML5_CONFIG ]; then + echo + echo "/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml (HTML5 client)" + echo " build: $(yq r $HTML5_CONFIG public.app.html5ClientBuild)" + echo " kurentoUrl: $(yq r $HTML5_CONFIG public.kurento.wsUrl)" + echo " enableListenOnly: $(yq r $HTML5_CONFIG public.kurento.enableListenOnly)" + fi - check_state - echo + check_state + echo - exit 0 + exit 0 fi # # Zip log files # if [ $ZIP ]; then - need_root - - LOG_FILE="$(date +'%Y%m%d')-$(date +%H)" - # - # Check log files - # - rm -f /tmp/$LOG_FILE.tar - rm -f /tmp/$LOG_FILE.tar.gz - rm -f /tmp/a - - touch /tmp/empty - tar cf /tmp/$LOG_FILE.tar /tmp/empty > /dev/null 2>&1 - tar rfh /tmp/$LOG_FILE.tar $RED5_DIR/log > /dev/null 2>&1 - tar rfh /tmp/$LOG_FILE.tar $SERVLET_LOGS > /dev/null 2>&1 - tar rf /tmp/$LOG_FILE.tar /var/log/bigbluebutton/* > /dev/null 2>&1 - tar rf /tmp/$LOG_FILE.tar /var/log/bbb-apps-akka > /dev/null 2>&1 - tar rf /tmp/$LOG_FILE.tar /var/log/bbb-transcode-akka > /dev/null 2>&1 - tar rf /tmp/$LOG_FILE.tar /var/log/bbb-fsesl-akka > /dev/null 2>&1 - tar rf /tmp/$LOG_FILE.tar /var/log/nginx/error.log > /dev/null 2>&1 - if [ $DISTRIB_ID == "ubuntu" ]; then - tar rf /tmp/$LOG_FILE.tar /var/log/syslog > /dev/null 2>&1 - fi - - tar tf /tmp/$LOG_FILE.tar - gzip /tmp/$LOG_FILE.tar - mv /tmp/$LOG_FILE.tar.gz /root/$LOG_FILE.tar.gz - echo - echo " Created: /root/$LOG_FILE.tar.gz" - echo + need_root + + LOG_FILE="$(date +'%Y%m%d')-$(date +%H).tar" + TMP_LOG_FILE="/tmp/$LOG_FILE" + # + # Check log files + # + rm -f "$LOG_FILE.gz" + rm -f /tmp/a + + touch /tmp/empty + tar cf $TMP_LOG_FILE /tmp/empty > /dev/null 2>&1 + tar rfh $TMP_LOG_FILE $RED5_DIR/log > /dev/null 2>&1 + tar rfh $TMP_LOG_FILE $SERVLET_LOGS > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/bigbluebutton/* > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/bbb-apps-akka > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/bbb-transcode-akka > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/bbb-fsesl-akka > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/bbb-webrtc-sfu > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/kurento-media-server > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/mongodb > /dev/null 2>&1 + tar rf $TMP_LOG_FILE /var/log/nginx/error.log > /dev/null 2>&1 + if [ $DISTRIB_ID == "ubuntu" ]; then + tar rf $TMP_LOG_FILE /var/log/syslog > /dev/null 2>&1 + fi + + tar tf $TMP_LOG_FILE + gzip $TMP_LOG_FILE + $SUDO mv $TMP_LOG_FILE.gz /root/$LOG_FILE.gz + echo + echo " Created: /root/$LOG_FILE.gz" + echo fi # # Check current setup # if [ $DEBUG ]; then - need_root - # - # Check log files - # - - rm -rf /tmp/t - grep --directories=skip ERROR /var/log/bigbluebutton/* > /tmp/t - if [ -s /tmp/t ]; then - echo " -- ERRORS found in /var/log/bigbluebutton/* -- " - cat /tmp/t - echo - fi - - rm -rf /tmp/t - grep --directories=skip Exception /var/log/bigbluebutton/* | grep -v CacheExceptionHandlerFactory > /tmp/t - if [ -s /tmp/t ]; then - echo " -- ERRORS found in /var/log/bigbluebutton/* -- " - cat /tmp/t - echo - fi + need_root + # + # Check log files + # + + rm -rf /tmp/t + grep --directories=skip ERROR /var/log/bigbluebutton/* > /tmp/t + if [ -s /tmp/t ]; then + echo " -- ERRORS found in /var/log/bigbluebutton/* -- " + cat /tmp/t + echo + fi + rm -rf /tmp/t + grep --directories=skip Exception /var/log/bigbluebutton/* | grep -v CacheExceptionHandlerFactory > /tmp/t + if [ -s /tmp/t ]; then + echo " -- ERRORS found in /var/log/bigbluebutton/* -- " + cat /tmp/t + echo + fi - rm -rf /tmp/t - grep --directories=skip ERROR $RED5_DIR/log/* > /tmp/t - if [ -s /tmp/t ]; then - echo " -- ERRORS found in $RED5_DIR/log/* -- " - cat /tmp/t - echo - fi + rm -rf /tmp/t + grep --directories=skip ERROR $RED5_DIR/log/* > /tmp/t + if [ -s /tmp/t ]; then + echo " -- ERRORS found in $RED5_DIR/log/* -- " + cat /tmp/t + echo + fi + + + rm -rf /tmp/t + grep --directories=skip Exception $RED5_DIR/log/* > /tmp/t + if [ -s /tmp/t ]; then + echo " -- Exceptions found in $RED5_DIR/log/* -- " + cat /tmp/t + echo + fi - rm -rf /tmp/t - grep --directories=skip Exception $RED5_DIR/log/* > /tmp/t - if [ -s /tmp/t ]; then - echo " -- Exceptions found in $RED5_DIR/log/* -- " - cat /tmp/t - echo - fi + rm -rf /tmp/t + $SUDO grep --directories=skip Exception $SERVLET_LOGS/* | grep -v CacheExceptionHandlerFactory > /tmp/t + if [ -s /tmp/t ]; then + echo " -- Exceptions found in $SERVLET_LOGS/ -- " + cat /tmp/t + echo + fi - rm -rf /tmp/t - $SUDO grep --directories=skip Exception $SERVLET_LOGS/* | grep -v CacheExceptionHandlerFactory > /tmp/t - if [ -s /tmp/t ]; then - echo " -- Exceptions found in $SERVLET_LOGS/ -- " - cat /tmp/t - echo - fi + rm -rf /tmp/t + if [ -s /var/log/nginx/error.log ]; then + cat /var/log/nginx/error.log | grep -v "/fcs/ident2" > /tmp/t + if [ -s /tmp/t ]; then + echo " -- Errors found in /var/log/nginx/error.log -- " + cat /tmp/t + echo + fi + fi + if [ $DISTRIB_ID == "Ubuntu" ]; then rm -rf /tmp/t - if [ -s /var/log/nginx/error.log ]; then - cat /var/log/nginx/error.log | grep -v "/fcs/ident2" > /tmp/t - if [ -s /tmp/t ]; then - echo " -- Errors found in /var/log/nginx/error.log -- " - cat /tmp/t - echo - fi + $SUDO grep --directories=skip -i exception /var/log/syslog > /tmp/t + if [ -s /tmp/t ]; then + echo " -- Errors found in /var/log/syslog -- " + cat /tmp/t + echo fi + fi - if [ $DISTRIB_ID == "Ubuntu" ]; then - rm -rf /tmp/t - $SUDO grep --directories=skip -i exception /var/log/syslog > /tmp/t - if [ -s /tmp/t ]; then - echo " -- Errors found in /var/log/syslog -- " - cat /tmp/t - echo - fi - fi - - rm -rf /tmp/t - if [ -d /var/log/bigbluebutton ]; then - $SUDO grep --directories=skip ERROR /var/log/bigbluebutton/* > /tmp/t - if [ -s /tmp/t ]; then - echo " -- Errors found in /var/log/bigbluebutton -- " - cat /tmp/t - echo - fi - fi - - rm -rf /tmp/t - if [ -d /var/log/bigbluebutton ]; then - $SUDO grep --directories=skip -i exception /var/log/bigbluebutton/* > /tmp/t - if [ -s /tmp/t ]; then - echo " -- Exceptions found in /var/log/bigbluebutton -- " - cat /tmp/t - echo - fi - fi - - # - # Additional checks for record and playback - # - - if [ -f /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml ]; then - bbb-record --check - fi - - exit 0 + rm -rf /tmp/t + if [ -d /var/log/bigbluebutton ]; then + $SUDO grep --directories=skip ERROR /var/log/bigbluebutton/* > /tmp/t + if [ -s /tmp/t ]; then + echo " -- Errors found in /var/log/bigbluebutton -- " + cat /tmp/t + echo + fi + fi + + rm -rf /tmp/t + if [ -d /var/log/bigbluebutton ]; then + $SUDO grep --directories=skip -i exception /var/log/bigbluebutton/* > /tmp/t + if [ -s /tmp/t ]; then + echo " -- Exceptions found in /var/log/bigbluebutton -- " + cat /tmp/t + echo + fi + fi + + # + # Additional checks for record and playback + # + + if [ -f /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml ]; then + bbb-record --check + fi + + exit 0 fi # if asked to print the version that's all we do if [ -n "$HOST" ]; then - # - # Just use the IP for port test in /var/www/bigbluebutton/client/conf/config.xml - # - echo "Assigning $HOST for testing for firewall in /var/www/bigbluebutton/client/conf/config.xml" - $SUDO sed -i "s/porttest host=\(\"[^\"]*\"\)/porttest host=\"$PROTOCOL_RTMP:\/\/$HOST\"/g" /var/www/bigbluebutton/client/conf/config.xml + # + # Just use the IP for port test in /var/www/bigbluebutton/client/conf/config.xml + # + echo "Assigning $HOST for testing for firewall in /var/www/bigbluebutton/client/conf/config.xml" + $SUDO sed -i "s/porttest host=\(\"[^\"]*\"\)/porttest host=\"$PROTOCOL_RTMP:\/\/$HOST\"/g" /var/www/bigbluebutton/client/conf/config.xml - echo "Assigning $HOST for rtmp:// in /var/www/bigbluebutton/client/conf/config.xml" - $SUDO sed -i "s/rtmp[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/$PROTOCOL_RTMP:\/\/$HOST\2/g" /var/www/bigbluebutton/client/conf/config.xml + echo "Assigning $HOST for rtmp:// in /var/www/bigbluebutton/client/conf/config.xml" + $SUDO sed -i "s/rtmp[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/$PROTOCOL_RTMP:\/\/$HOST\2/g" /var/www/bigbluebutton/client/conf/config.xml - echo "Assigning $HOST for servername in /etc/nginx/sites-available/bigbluebutton" - $SUDO sed -i "s/server_name .*/server_name $HOST;/g" /etc/nginx/sites-available/bigbluebutton + echo "Assigning $HOST for servername in /etc/nginx/sites-available/bigbluebutton" + $SUDO sed -i "s/server_name .*/server_name $HOST;/g" /etc/nginx/sites-available/bigbluebutton - # - # Update configuration for BigBlueButton client (and preserve hostname for chromeExtensionLink if exists) - # + # + # Update configuration for BigBlueButton client (and preserve hostname for chromeExtensionLink if exists) + # - # Extract the chrome store URL before updating config.xml. We will be able to restore it. - chromeExtensionLinkURL=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/chromeExtensionLink/{s/.*https*:\/\///;s/\/.*//;p}') - - echo "Assigning $HOST for http[s]:// in /var/www/bigbluebutton/client/conf/config.xml" - $SUDO sed -i "s/http[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/$PROTOCOL:\/\/$HOST\2/g" \ - /var/www/bigbluebutton/client/conf/config.xml + # Extract the chrome store URL before updating config.xml. We will be able to restore it. + chromeExtensionLinkURL=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/chromeExtensionLink/{s/.*https*:\/\///;s/\/.*//;p}') - if ! echo "$chromeExtensionLinkURL" | grep -q '""'; then - $SUDO sed -i "s/chromeExtensionLink=\"https:\/\/[^\/]*/chromeExtensionLink=\"https:\/\/$chromeExtensionLinkURL/g" \ - /var/www/bigbluebutton/client/conf/config.xml - fi + echo "Assigning $HOST for http[s]:// in /var/www/bigbluebutton/client/conf/config.xml" + $SUDO sed -i "s/http[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/$PROTOCOL:\/\/$HOST\2/g" \ + /var/www/bigbluebutton/client/conf/config.xml - echo "Assigning $HOST for publishURI in /var/www/bigbluebutton/client/conf/config.xml" - $SUDO sed -i "s/publishURI=\"[^\"]*\"/publishURI=\"$HOST\"/" /var/www/bigbluebutton/client/conf/config.xml + if ! echo "$chromeExtensionLinkURL" | grep -q '""'; then + $SUDO sed -i "s/chromeExtensionLink=\"https:\/\/[^\/]*/chromeExtensionLink=\"https:\/\/$chromeExtensionLinkURL/g" \ + /var/www/bigbluebutton/client/conf/config.xml + fi - # - # Update configuration for BigBlueButton web app - # - echo "Assigning $HOST for web application URL in ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties" + echo "Assigning $HOST for publishURI in /var/www/bigbluebutton/client/conf/config.xml" + $SUDO sed -i "s/publishURI=\"[^\"]*\"/publishURI=\"$HOST\"/" /var/www/bigbluebutton/client/conf/config.xml - $SUDO sed -i "s/bigbluebutton.web.serverURL=http[s]*:\/\/.*/bigbluebutton.web.serverURL=$PROTOCOL:\/\/$HOST/g" \ - ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties + # + # Update configuration for BigBlueButton web app + # + echo "Assigning $HOST for web application URL in ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties" - $SUDO sed -i "s/screenshareRtmpServer=.*/screenshareRtmpServer=$HOST/g" \ - ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties + $SUDO sed -i "s/bigbluebutton.web.serverURL=http[s]*:\/\/.*/bigbluebutton.web.serverURL=$PROTOCOL:\/\/$HOST/g" \ + ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties - change_var_value /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties streamBaseUrl rtmp://$HOST/screenshare - change_var_value /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties jnlpUrl $PROTOCOL://$HOST/screenshare - change_var_value /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties jnlpFile $PROTOCOL://$HOST/screenshare/screenshare.jnlp + $SUDO sed -i "s/screenshareRtmpServer=.*/screenshareRtmpServer=$HOST/g" \ + ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties - if ! grep -q server_names_hash_bucket_size /etc/nginx/nginx.conf; then - $SUDO sed -i "s/gzip on;/gzip on;\n server_names_hash_bucket_size 64;/g" /etc/nginx/nginx.conf - fi + change_var_value /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties streamBaseUrl rtmp://$HOST/screenshare + change_var_value /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties jnlpUrl $PROTOCOL://$HOST/screenshare + change_var_value /usr/share/red5/webapps/screenshare/WEB-INF/screenshare.properties jnlpFile $PROTOCOL://$HOST/screenshare/screenshare.jnlp - # - # Update bbb-apps-akka - # - echo "Assigning $HOST for web application URL in /usr/share/bbb-apps-akka/conf/application.conf" - - if [ -f /usr/share/bbb-apps-akka/conf/application.conf ]; then - sed -i "s/bbbWebAPI[ ]*=[ ]*\"[^\"]*\"/bbbWebAPI=\"${PROTOCOL}:\/\/$HOST\/bigbluebutton\/api\"/g" \ - /usr/share/bbb-apps-akka/conf/application.conf - sed -i "s/deskshareip[ ]*=[ ]*\"[^\"]*\"/deskshareip=\"$HOST\"/g" \ - /usr/share/bbb-apps-akka/conf/application.conf - # Fix to ensure application.conf has the latest shared secret - SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2); - sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SECRET\"/g" \ - /usr/share/bbb-apps-akka/conf/application.conf - fi + if ! grep -q server_names_hash_bucket_size /etc/nginx/nginx.conf; then + $SUDO sed -i "s/gzip on;/gzip on;\n server_names_hash_bucket_size 64;/g" /etc/nginx/nginx.conf + fi - # - # Update api demos - # - if [ -f ${TOMCAT_DIR}/webapps/demo/bbb_api_conf.jsp ]; then - echo "Assigning $HOST for api demos in ${TOMCAT_DIR}/webapps/demo/bbb_api_conf.jsp" - $SUDO sed -i "s/BigBlueButtonURL = \"http[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/BigBlueButtonURL = \"$PROTOCOL:\/\/$HOST\2/g" \ - ${TOMCAT_DIR}/webapps/demo/bbb_api_conf.jsp - fi + # + # Update bbb-apps-akka + # + echo "Assigning $HOST for web application URL in /usr/share/bbb-apps-akka/conf/application.conf" + + if [ -f /usr/share/bbb-apps-akka/conf/application.conf ]; then + sed -i "s/bbbWebAPI[ ]*=[ ]*\"[^\"]*\"/bbbWebAPI=\"${PROTOCOL}:\/\/$HOST\/bigbluebutton\/api\"/g" \ + /usr/share/bbb-apps-akka/conf/application.conf + sed -i "s/deskshareip[ ]*=[ ]*\"[^\"]*\"/deskshareip=\"$HOST\"/g" \ + /usr/share/bbb-apps-akka/conf/application.conf + # Fix to ensure application.conf has the latest shared secret + SECRET=$(cat ${SERVLET_DIR}/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2); + sed -i "s/sharedSecret[ ]*=[ ]*\"[^\"]*\"/sharedSecret=\"$SECRET\"/g" \ + /usr/share/bbb-apps-akka/conf/application.conf + fi - - - if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then - echo "Assigning $HOST for LTI integration in ${LTI_DIR}/WEB-INF/classes/lti-config.properties" - # We don't wat to guess on http/https as the lti endpoint may be a different BigBlueButton server - sed -i "s/bigbluebuttonURL=http:\/\/.*/bigbluebuttonURL=http:\/\/$HOST\/bigbluebutton/g" \ - ${LTI_DIR}/WEB-INF/classes/lti-config.properties - sed -i "s/bigbluebuttonURL=https:\/\/.*/bigbluebuttonURL=https:\/\/$HOST\/bigbluebutton/g" \ - ${LTI_DIR}/WEB-INF/classes/lti-config.properties - sed -i "s/ltiEndPoint=.*/ltiEndPoint=$HOST/g" \ - ${LTI_DIR}/WEB-INF/classes/lti-config.properties - fi - - - if [ -f /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml ]; then - echo "Assigning $HOST for record and playback in /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml" - change_yml_value /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml $redis_host playback_host $HOST - fi - - echo -n "Assigning $HOST for playback of recordings: " - for metadata in $(find /var/bigbluebutton/published -name metadata.xml); do - echo -n "." - # Ensure we update both types of URLs - sed -i "/<link>/{s/http:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/http:\/\/$HOST\/playback\/$type\2/g}" $metadata - sed -i "/<link>/{s/https:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/https:\/\/$HOST\/playback\/$type\2/g}" $metadata - done + # + # Update api demos + # + if [ -f ${TOMCAT_DIR}/webapps/demo/bbb_api_conf.jsp ]; then + echo "Assigning $HOST for api demos in ${TOMCAT_DIR}/webapps/demo/bbb_api_conf.jsp" + $SUDO sed -i "s/BigBlueButtonURL = \"http[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/BigBlueButtonURL = \"$PROTOCOL:\/\/$HOST\2/g" \ + ${TOMCAT_DIR}/webapps/demo/bbb_api_conf.jsp + fi - for metadata in $(find /var/bigbluebutton/unpublished -name metadata.xml); do - echo -n "." - # Ensure we update both types of URLs - sed -i "/<link>/{s/http:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/http:\/\/$HOST\/playback\/$type\2/g}" $metadata - sed -i "/<link>/{s/https:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/https:\/\/$HOST\/playback\/$type\2/g}" $metadata - done - echo - if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then - echo "Assigning $HOST for client self check to /var/www/bigbluebutton/check/conf/config.xml" - sed -i "s/rtmp[s]*:\/\/\([^\"\/]*\)\//$PROTOCOL_RTMP:\/\/$HOST\//g" /var/www/bigbluebutton/check/conf/config.xml - sed -i "s/rtmpt:\/\/\([^\"\/]*\)\//rtmpt:\/\/$HOST\//g" /var/www/bigbluebutton/check/conf/config.xml - fi - # - # Update HTML5 client - # - if [ -f $HTML5_CONFIG_OLD ]; then - WS=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | sed 's/https/wss/g' | sed s'/http/ws/g') - sed -i "s|\"wsUrl.*|\"wsUrl\": \"$WS/bbb-webrtc-sfu\",|g" $HTML5_CONFIG_OLD - fi + if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then + echo "Assigning $HOST for LTI integration in ${LTI_DIR}/WEB-INF/classes/lti-config.properties" + # We don't wat to guess on http/https as the lti endpoint may be a different BigBlueButton server + sed -i "s/bigbluebuttonURL=http:\/\/.*/bigbluebuttonURL=http:\/\/$HOST\/bigbluebutton/g" \ + ${LTI_DIR}/WEB-INF/classes/lti-config.properties + sed -i "s/bigbluebuttonURL=https:\/\/.*/bigbluebuttonURL=https:\/\/$HOST\/bigbluebutton/g" \ + ${LTI_DIR}/WEB-INF/classes/lti-config.properties + sed -i "s/ltiEndPoint=.*/ltiEndPoint=$HOST/g" \ + ${LTI_DIR}/WEB-INF/classes/lti-config.properties + fi - if [ -f $HTML5_CONFIG ]; then - WS=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | sed 's/https/wss/g' | sed s'/http/ws/g') - change_yml_value $HTML5_CONFIG wsUrl "$WS/bbb-webrtc-sfu" + if [ -f /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml ]; then + echo "Assigning $HOST for record and playback in /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml" + change_yml_value /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml $redis_host playback_host $HOST + fi - if [ -f $KURENTO_CONFIG ]; then - yq w -i $KURENTO_CONFIG kurento[0].url "ws://$HOST:8888/kurento" - chown bigbluebutton:bigbluebutton $KURENTO_CONFIG - fi - fi + echo -n "Assigning $HOST for playback of recordings: " + for metadata in $(find /var/bigbluebutton/published -name metadata.xml); do + echo -n "." + # Ensure we update both types of URLs + sed -i "/<link>/{s/http:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/http:\/\/$HOST\/playback\/$type\2/g}" $metadata + sed -i "/<link>/{s/https:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/https:\/\/$HOST\/playback\/$type\2/g}" $metadata + done + + for metadata in $(find /var/bigbluebutton/unpublished -name metadata.xml); do + echo -n "." + # Ensure we update both types of URLs + sed -i "/<link>/{s/http:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/http:\/\/$HOST\/playback\/$type\2/g}" $metadata + sed -i "/<link>/{s/https:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/https:\/\/$HOST\/playback\/$type\2/g}" $metadata + done + echo + + if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then + echo "Assigning $HOST for client self check to /var/www/bigbluebutton/check/conf/config.xml" + sed -i "s/rtmp[s]*:\/\/\([^\"\/]*\)\//$PROTOCOL_RTMP:\/\/$HOST\//g" /var/www/bigbluebutton/check/conf/config.xml + sed -i "s/rtmpt:\/\/\([^\"\/]*\)\//rtmpt:\/\/$HOST\//g" /var/www/bigbluebutton/check/conf/config.xml + fi - # - # Update thumbnail links - # - find /var/bigbluebutton/published -name metadata.xml -exec \ - sed -i "s/<image width=\"\([0-9]*\)\" height=\"\([0-9]*\)\" alt=\"\([^\"]*\)\">\(http[s]*\):\/\/[^\/]*\/\(.*\)/<image width=\"\1\" height=\"\2\" alt=\"\3\">\4:\/\/$HOST\/\5/g" \ - '{}' \; + # + # Update HTML5 client + # + if [ -f $HTML5_CONFIG_OLD ]; then + WS=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | sed 's/https/wss/g' | sed s'/http/ws/g') + sed -i "s|\"wsUrl.*|\"wsUrl\": \"$WS/bbb-webrtc-sfu\",|g" $HTML5_CONFIG_OLD + fi + + if [ -f $HTML5_CONFIG ]; then + WS=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | sed 's/https/wss/g' | sed s'/http/ws/g') - echo "Restarting the BigBlueButton $BIGBLUEBUTTON_RELEASE ..." - stop_bigbluebutton - echo - start_bigbluebutton + yq w -i $HTML5_CONFIG public.kurento.wsUrl "$WS/bbb-webrtc-sfu" + yq w -i $HTML5_CONFIG public.note.url "$PROTOCOL://$HOST/pad" + chown meteor:meteor $HTML5_CONFIG - exit 0 + if [ -f $KURENTO_CONFIG ]; then + yq w -i $KURENTO_CONFIG kurento[0].url "ws://$HOST:8888/kurento" + chown bigbluebutton:bigbluebutton $KURENTO_CONFIG + fi + fi + + # + # Update thumbnail links + # + find /var/bigbluebutton/published -name metadata.xml -exec \ + sed -i "s/<image width=\"\([0-9]*\)\" height=\"\([0-9]*\)\" alt=\"\([^\"]*\)\">\(http[s]*\):\/\/[^\/]*\/\(.*\)/<image width=\"\1\" height=\"\2\" alt=\"\3\">\4:\/\/$HOST\/\5/g" \ + '{}' \; + + echo "Restarting the BigBlueButton $BIGBLUEBUTTON_RELEASE ..." + stop_bigbluebutton + echo + start_bigbluebutton + + exit 0 fi if [ $RESTART ]; then - need_root - check_configuration - - echo "Restarting BigBlueButton $BIGBLUEBUTTON_RELEASE ..." - - stop_bigbluebutton - start_bigbluebutton - check_state + need_root + check_configuration + + echo "Restarting BigBlueButton $BIGBLUEBUTTON_RELEASE ..." + + stop_bigbluebutton + start_bigbluebutton + check_state fi if [ $CLEAN ]; then - need_root - check_configuration - - echo "Restarting BigBlueButton $BIGBLUEBUTTON_RELEASE (and cleaning out all log files) ..." - - stop_bigbluebutton - - # - # Clean log files - # - - echo " ... cleaning log files" - rm -f /var/log/bigbluebutton/bbb-web.log* - rm -f /var/log/bigbluebutton/*.log - - rm -f /opt/freeswitch/var/log/freeswitch/*.log - - # - # Clean out the log files for record and playback - # - rm -f /var/log/bigbluebutton/bbb-rap-worker.log* - rm -f /var/log/bigbluebutton/archive.log* - if [ -d /var/log/bigbluebutton/html5 ]; then - rm -f /var/log/bigbluebutton/html5/* - fi - - if [ -d /var/log/bigbluebutton/podcast ]; then - rm -f /var/log/bigbluebutton/podcast/* - fi - - if [ -d /var/log/bigbluebutton/presentation ]; then - rm -f /var/log/bigbluebutton/presentation/* - fi - - if [ -d /var/log/bigbluebutton/screenshare ]; then - rm -f /var/log/bigbluebutton/screenshare/* - fi - - - if [ $RED5_DIR ]; then - rm -rf $RED5_DIR/log/* - fi - - if [ $SERVLET_LOGS ]; then - rm -rf $SERVLET_LOGS/* - fi - - rm -rf /var/log/nginx/* - - #if [ $DISTRO == "ubuntu" ]; then - # mv /var/log/syslog /tmp/syslog.$$ - # echo "" > /var/log/syslog - # chown syslog:adm /var/log/syslog - #fi - - if [ -d /var/log/bbb-fsesl-akka ]; then - rm -f /var/log/bbb-fsesl-akka/* - fi + need_root + check_configuration - if [ -d /var/log/bbb-apps-akka ]; then - rm -f /var/log/bbb-apps-akka/* - fi + echo "Restarting BigBlueButton $BIGBLUEBUTTON_RELEASE (and cleaning out all log files) ..." + + stop_bigbluebutton - if [ -d /var/log/bbb-transcode-akka ]; then - rm -f /var/log/bbb-transcode-akka/* - fi + # + # Clean log files + # - if [ -d /var/log/bbb-webrtc-sfu ]; then - rm -f /var/log/bbb-webrtc-sfu/* - fi - - if [ -d /var/log/redis ]; then - rm -f /var/log/redis/* - fi + echo " ... cleaning log files" + rm -f /var/log/bigbluebutton/*.log - if [ -d /var/log/mongodb ]; then - rm -f /var/log/mongodb/* - fi + rm -f /opt/freeswitch/var/log/freeswitch/*.log - if [ -d /var/log/kurento-media-server ]; then - rm -f /var/log/kurento-media-server/* - fi + # + # Clean out the log files for record and playback + # + rm -f /var/log/bigbluebutton/bbb-rap-worker.log* + rm -f /var/log/bigbluebutton/archive.log* + if [ -d /var/log/bigbluebutton/html5 ]; then + rm -f /var/log/bigbluebutton/html5/* + fi + + if [ -d /var/log/bigbluebutton/podcast ]; then + rm -f /var/log/bigbluebutton/podcast/* + fi + + if [ -d /var/log/bigbluebutton/presentation ]; then + rm -f /var/log/bigbluebutton/presentation/* + fi + + if [ -d /var/log/bigbluebutton/screenshare ]; then + rm -f /var/log/bigbluebutton/screenshare/* + fi - start_bigbluebutton - check_state + + if [[ $RED5_DIR ]]; then + rm -rf $RED5_DIR/log/* + fi + + if [[ $SERVLET_LOGS ]]; then + rm -rf $SERVLET_LOGS/* + fi + + rm -rf /var/log/nginx/* + + #if [ $DISTRO == "ubuntu" ]; then + # mv /var/log/syslog /tmp/syslog.$$ + # echo "" > /var/log/syslog + # chown syslog:adm /var/log/syslog + #fi + + log_dirs="bbb-fsesl-akka bbb-apps-akka bbb-transcode-akka bbb-webrtc-sfu redis mongodb kurento-media-server" + + for log_dir in $log_dirs; do + if [ -d "/var/log/$log_dir" ]; then + rm -f "/var/log/$log_dir/*" + fi + done + + start_bigbluebutton + check_state fi if [ $NETWORK ]; then - netstat -ant | egrep ":1935|:80\ " | egrep -v ":::|0.0.0.0" > /tmp/t_net - REMOTE=$(cat /tmp/t_net | cut -c 45-68 | cut -d ":" -f1 | sort | uniq) - - if [ "$REMOTE" != "" ]; then - echo -e "netstat\t\t\t80\t1935" - for IP in $REMOTE ; do - PORT_1935=$(cat /tmp/t_net | grep :1935 | cut -c 45-68 | cut -d ":" -f1 | grep $IP | wc -l) - PORT_80=$(cat /tmp/t_net | grep :80 | cut -c 45-68 | cut -d ":" -f1 | grep $IP | wc -l ) - - echo -e "$IP\t\t$PORT_80\t$PORT_1935" - done - fi + netstat -ant | egrep ":1935|:80\ " | egrep -v ":::|0.0.0.0" > /tmp/t_net + REMOTE=$(cat /tmp/t_net | cut -c 45-68 | cut -d ":" -f1 | sort | uniq) + + if [ "$REMOTE" != "" ]; then + echo -e "netstat\t\t\t80\t1935" + for IP in $REMOTE ; do + PORT_1935=$(cat /tmp/t_net | grep :1935 | cut -c 45-68 | cut -d ":" -f1 | grep $IP | wc -l) + PORT_80=$(cat /tmp/t_net | grep :80 | cut -c 45-68 | cut -d ":" -f1 | grep $IP | wc -l ) + + echo -e "$IP\t\t$PORT_80\t$PORT_1935" + done + fi fi if [ $WATCH ]; then - need_root - watch -n 2 "top -n 1 -b | head -n 5; echo; bbb-conf --network; bbb-conf --debug" + need_root + watch -n 2 "top -n 1 -b | head -n 5; echo; bbb-conf --network; bbb-conf --debug" fi diff --git a/bigbluebutton-html5/.gitignore b/bigbluebutton-html5/.gitignore index 535d3f9662258cbd3f8fd64048419b9a2ee85907..56e914594c2d02c8dfede9e22cca61a802abd6e4 100755 --- a/bigbluebutton-html5/.gitignore +++ b/bigbluebutton-html5/.gitignore @@ -1,17 +1,5 @@ -app/packages -app/build/ +.dbshell npm-debug.log node_modules/ .meteor/dev_bundle -private/config/server/* -!private/config/server/app.yaml -!private/config/server/redis.yaml -!private/config/server/shell.yaml - -private/config/development/server/* -!private/config/development/server/log.yaml - -private/config/production/server/* -!private/config/production/server/log.yaml -!private/config/production/server/shell.yaml diff --git a/bigbluebutton-html5/imports/api/breakouts/server/publishers.js b/bigbluebutton-html5/imports/api/breakouts/server/publishers.js index 41dade1937c86f23f7a4d0f1d90a0b012305c31e..73c427f421c2cb54d8a6aad5c225a82feabf34f0 100755 --- a/bigbluebutton-html5/imports/api/breakouts/server/publishers.js +++ b/bigbluebutton-html5/imports/api/breakouts/server/publishers.js @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import Breakouts from '/imports/api/breakouts'; +import Users from '/imports/api/users'; import Logger from '/imports/startup/server/logger'; -function breakouts(credentials, moderator) { +function breakouts(credentials, moderator = false) { const { meetingId, requesterUserId, @@ -10,14 +11,17 @@ function breakouts(credentials, moderator) { Logger.debug(`Publishing Breakouts for ${meetingId} ${requesterUserId}`); if (moderator) { - const presenterSelector = { - $or: [ - { parentMeetingId: meetingId }, - { breakoutId: meetingId }, - ], - }; + const User = Users.findOne({ userId: requesterUserId }); + if (!!User && User.moderator) { + const presenterSelector = { + $or: [ + { parentMeetingId: meetingId }, + { breakoutId: meetingId }, + ], + }; - return Breakouts.find(presenterSelector); + return Breakouts.find(presenterSelector); + } } const selector = { diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/server/eventHandlers.js b/bigbluebutton-html5/imports/api/group-chat-msg/server/eventHandlers.js index acc656abb290929dfe3bfa7c7c8ac4a57c304a93..e5aeea22f9dc408e00d0f97153ded1015bbf8011 100644 --- a/bigbluebutton-html5/imports/api/group-chat-msg/server/eventHandlers.js +++ b/bigbluebutton-html5/imports/api/group-chat-msg/server/eventHandlers.js @@ -2,8 +2,9 @@ import RedisPubSub from '/imports/startup/server/redis'; import handleGroupChatsMsgs from './handlers/groupChatsMsgs'; import handleGroupChatMsgBroadcast from './handlers/groupChatMsgBroadcast'; import handleClearPublicGroupChat from './handlers/clearPublicGroupChat'; +import { processForHTML5ServerOnly } from '/imports/api/common/server/helpers'; -RedisPubSub.on('GetGroupChatMsgsRespMsg', handleGroupChatsMsgs); +RedisPubSub.on('GetGroupChatMsgsRespMsg', processForHTML5ServerOnly(handleGroupChatsMsgs)); RedisPubSub.on('GroupChatMessageBroadcastEvtMsg', handleGroupChatMsgBroadcast); RedisPubSub.on('ClearPublicChatHistoryEvtMsg', handleClearPublicGroupChat); RedisPubSub.on('SyncGetGroupChatMsgsRespMsg', handleGroupChatsMsgs); diff --git a/bigbluebutton-html5/imports/api/group-chat/server/eventHandlers.js b/bigbluebutton-html5/imports/api/group-chat/server/eventHandlers.js index 8d9b08f80e0930e726c9ed831c4a605fc44c3e33..c676a06732ad0516ec9cbac3c154e4d051c46f4d 100644 --- a/bigbluebutton-html5/imports/api/group-chat/server/eventHandlers.js +++ b/bigbluebutton-html5/imports/api/group-chat/server/eventHandlers.js @@ -2,8 +2,9 @@ import RedisPubSub from '/imports/startup/server/redis'; import handleGroupChats from './handlers/groupChats'; import handleGroupChatCreated from './handlers/groupChatCreated'; import handleGroupChatDestroyed from './handlers/groupChatDestroyed'; +import { processForHTML5ServerOnly } from '/imports/api/common/server/helpers'; -RedisPubSub.on('GetGroupChatsRespMsg', handleGroupChats); +RedisPubSub.on('GetGroupChatsRespMsg', processForHTML5ServerOnly(handleGroupChats)); RedisPubSub.on('GroupChatCreatedEvtMsg', handleGroupChatCreated); RedisPubSub.on('GroupChatDestroyedEvtMsg', handleGroupChatDestroyed); RedisPubSub.on('SyncGetGroupChatsRespMsg', handleGroupChats); diff --git a/bigbluebutton-html5/imports/api/meetings/server/publishers.js b/bigbluebutton-html5/imports/api/meetings/server/publishers.js index 9bd0bf3ae8e2e57e236099e0f95b51c4c69a37c2..b263a5eb3e8bad28fc2deeadb73f1d035d1d4ecc 100755 --- a/bigbluebutton-html5/imports/api/meetings/server/publishers.js +++ b/bigbluebutton-html5/imports/api/meetings/server/publishers.js @@ -1,9 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import Meetings from '/imports/api/meetings'; +import Users from '/imports/api/users'; import Logger from '/imports/startup/server/logger'; -function meetings(credentials) { +function meetings(credentials, isModerator = false) { const { meetingId, requesterUserId, requesterToken } = credentials; check(meetingId, String); @@ -15,13 +16,19 @@ function meetings(credentials) { const selector = { $or: [ { meetingId }, - { - 'meetingProp.isBreakout': true, - 'breakoutProps.parentId': meetingId, - }, ], }; + if (isModerator) { + const User = Users.findOne({ userId: requesterUserId }); + if (!!User && User.moderator) { + selector.$or.push({ + 'meetingProp.isBreakout': true, + 'breakoutProps.parentId': meetingId, + }); + } + } + const options = { fields: { password: false, diff --git a/bigbluebutton-html5/imports/api/screenshare/server/modifiers/clearScreenshare.js b/bigbluebutton-html5/imports/api/screenshare/server/modifiers/clearScreenshare.js index 21e2353f689def41992920e5ae72b8138109d39a..0b7265a69716656b8ef3ac7c5407fc2973ef9acc 100644 --- a/bigbluebutton-html5/imports/api/screenshare/server/modifiers/clearScreenshare.js +++ b/bigbluebutton-html5/imports/api/screenshare/server/modifiers/clearScreenshare.js @@ -2,9 +2,16 @@ import Logger from '/imports/startup/server/logger'; import Screenshare from '/imports/api/screenshare'; export default function clearScreenshare(meetingId, screenshareConf) { + const cb = (err) => { + if (err) { + return Logger.error(`removing screenshare to collection: ${err}`); + } + + return Logger.info(`removed screenshare meetingId=${meetingId} id=${screenshareConf}`); + }; + if (meetingId && screenshareConf) { - return Screenshare.remove({ meetingId, 'screenshare.screenshareConf': screenshareConf }, Logger.info(`Cleared Screenshare (${meetingId}) , (${screenshareConf})`)); + return Screenshare.remove({ meetingId, 'screenshare.screenshareConf': screenshareConf }, cb); } - - return Screenshare.remove({}, Logger.info('Cleared Screenshare (all)')); + return Screenshare.remove({}, cb); } diff --git a/bigbluebutton-html5/imports/api/users/server/eventHandlers.js b/bigbluebutton-html5/imports/api/users/server/eventHandlers.js index 8889f998475338980db3f80baa938c6d38c7a218..7af6c464e6488eaf1a69a87f536ec872d1ad30e6 100644 --- a/bigbluebutton-html5/imports/api/users/server/eventHandlers.js +++ b/bigbluebutton-html5/imports/api/users/server/eventHandlers.js @@ -17,4 +17,4 @@ RedisPubSub.on('UserEmojiChangedEvtMsg', handleEmojiStatus); RedisPubSub.on('SyncGetUsersMeetingRespMsg', handleGetUsers); RedisPubSub.on('UserEjectedFromMeetingEvtMsg', handleUserEjected); RedisPubSub.on('UserRoleChangedEvtMsg', handleChangeRole); -RedisPubSub.on('UserInactivityInspectMsg', handleUserInactivityInspect); \ No newline at end of file +RedisPubSub.on('UserInactivityInspectMsg', handleUserInactivityInspect); diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/userEjected.js b/bigbluebutton-html5/imports/api/users/server/handlers/userEjected.js index 56ee89ae7e1582086b3e65cddf766dbc4eb4a45a..95568f555fc2eced0027f077873afbcd26861e43 100644 --- a/bigbluebutton-html5/imports/api/users/server/handlers/userEjected.js +++ b/bigbluebutton-html5/imports/api/users/server/handlers/userEjected.js @@ -1,7 +1,8 @@ import userEjected from '../modifiers/userEjected'; -export default function handleEjectedUser({ header }) { +export default function handleEjectedUser({ header, body }) { const { meetingId, userId } = header; + const { reasonCode } = body; - return userEjected(meetingId, userId); + return userEjected(meetingId, userId, reasonCode); } diff --git a/bigbluebutton-html5/imports/api/users/server/modifiers/userEjected.js b/bigbluebutton-html5/imports/api/users/server/modifiers/userEjected.js index ebca105c2ba4b5c6acb2e7e61f01a49966435459..cbe59a0ea5cd129a6bd0ab4375350627689eca52 100644 --- a/bigbluebutton-html5/imports/api/users/server/modifiers/userEjected.js +++ b/bigbluebutton-html5/imports/api/users/server/modifiers/userEjected.js @@ -2,9 +2,10 @@ import { check } from 'meteor/check'; import Logger from '/imports/startup/server/logger'; import Users from '/imports/api/users'; -export default function userEjected(meetingId, userId) { +export default function userEjected(meetingId, userId, ejectedReason) { check(meetingId, String); check(userId, String); + check(ejectedReason, String); const selector = { meetingId, @@ -14,6 +15,7 @@ export default function userEjected(meetingId, userId) { const modifier = { $set: { ejected: true, + ejectedReason, }, }; @@ -23,7 +25,7 @@ export default function userEjected(meetingId, userId) { } if (numChanged) { - return Logger.info(`Ejected user id=${userId} meeting=${meetingId}`); + return Logger.info(`Ejected user id=${userId} meeting=${meetingId} reason=${ejectedReason}`); } return null; diff --git a/bigbluebutton-html5/imports/api/users/server/publishers.js b/bigbluebutton-html5/imports/api/users/server/publishers.js index c6692046f59ef930feb859b409687afa7d77234c..e26190a1f1d9cda67948d4514883c16df8924bc4 100644 --- a/bigbluebutton-html5/imports/api/users/server/publishers.js +++ b/bigbluebutton-html5/imports/api/users/server/publishers.js @@ -57,11 +57,14 @@ function users(credentials, isModerator = false) { }; if (isModerator) { - selector.$or.push({ - 'breakoutProps.isBreakoutUser': true, - 'breakoutProps.parentId': meetingId, - connectionStatus: 'online', - }); + const User = Users.findOne({ userId: requesterUserId }); + if (!!User && User.moderator) { + selector.$or.push({ + 'breakoutProps.isBreakoutUser': true, + 'breakoutProps.parentId': meetingId, + connectionStatus: 'online', + }); + } } const options = { diff --git a/bigbluebutton-html5/imports/api/voice-users/server/modifiers/updateVoiceUser.js b/bigbluebutton-html5/imports/api/voice-users/server/modifiers/updateVoiceUser.js index 518010b0c08903ea8a9a2889f96c3e2d0d78e263..226a4087fc4426ac935dfa3166f48be8423ad690 100644 --- a/bigbluebutton-html5/imports/api/voice-users/server/modifiers/updateVoiceUser.js +++ b/bigbluebutton-html5/imports/api/voice-users/server/modifiers/updateVoiceUser.js @@ -33,7 +33,7 @@ export default function updateVoiceUser(meetingId, voiceUser) { return Logger.error(`Update voiceUser=${intId}: ${err}`); } - return Logger.info(`Update voiceUser=${intId} meeting=${meetingId}`); + return Logger.debug(`Update voiceUser=${intId} meeting=${meetingId}`); }; return VoiceUsers.update(selector, modifier, cb); diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index 72c639cdfcc90430a0cfadcd96111ba71f32125f..4faee3bb804283944286d3df07ee62c357506f69 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -144,6 +144,8 @@ class Base extends Component { const { updateLoadingState } = this; const stateControls = { updateLoadingState }; + const { ejected } = this.props; + const { loading, meetingExisted } = this.state; const codeError = Session.get('codeError'); @@ -152,7 +154,13 @@ class Base extends Component { meetingExist, } = this.props; - if (meetingExisted && !meetingExist) { + if (ejected && ejected.ejectedReason) { + const { ejectedReason } = ejected; + AudioManager.exitAudio(); + return (<MeetingEnded code={ejectedReason} />); + } + + if ((meetingExisted && !meetingExist)) { AudioManager.exitAudio(); return (<MeetingEnded code={Session.get('codeError')} />); } @@ -201,6 +209,7 @@ const BaseContainer = withTracker(() => { const { credentials, loggedIn } = Auth; const { meetingId, requesterUserId } = credentials; let breakoutRoomSubscriptionHandler; + let meetingModeratorSubscriptionHandler; let userSubscriptionHandler; const subscriptionErrorHandler = { @@ -238,6 +247,7 @@ const BaseContainer = withTracker(() => { // override meteor subscription to verify if is moderator userSubscriptionHandler = Meteor.subscribe('users', credentials, mappedUser.isModerator, subscriptionErrorHandler); breakoutRoomSubscriptionHandler = Meteor.subscribe('breakouts', credentials, mappedUser.isModerator, subscriptionErrorHandler); + breakoutRoomSubscriptionHandler = Meteor.subscribe('meetings', credentials, mappedUser.isModerator, subscriptionErrorHandler); } const annotationsHandler = Meteor.subscribe('annotations', credentials, { @@ -265,6 +275,7 @@ const BaseContainer = withTracker(() => { groupChatMessageHandler, userSubscriptionHandler, breakoutRoomSubscriptionHandler, + meetingModeratorSubscriptionHandler, animations, meetingExist: !!Meetings.find({ meetingId }).count(), User, diff --git a/bigbluebutton-html5/imports/ui/components/activity-check/component.jsx b/bigbluebutton-html5/imports/ui/components/activity-check/component.jsx index fc182c17eced6cfa551e71a90b7be7515bb17fc5..ce8e41ec6f43e984920d84081ee1b406b9e52bee 100644 --- a/bigbluebutton-html5/imports/ui/components/activity-check/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/activity-check/component.jsx @@ -42,9 +42,11 @@ class ActivityCheck extends Component { this.stopRemainingTime = this.stopRemainingTime.bind(this); this.updateRemainingTime = this.updateRemainingTime.bind(this); + this.playAudioAlert = this.playAudioAlert.bind(this); } componentDidMount() { + this.playAudioAlert(); this.interval = this.updateRemainingTime(); } @@ -72,6 +74,11 @@ class ActivityCheck extends Component { clearInterval(this.interval); } + playAudioAlert() { + this.alert = new Audio(`${Meteor.settings.public.app.basename}/resources/sounds/notify.mp3`); + this.alert.play(); + } + render() { const { intl } = this.props; @@ -80,6 +87,7 @@ class ActivityCheck extends Component { return ( <Modal hideBorder + onRequestClose={handleInactivityDismiss} shouldCloseOnOverlayClick={false} shouldShowCloseButton={false} > diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx index 50fcc12a52ab026aa9f143a7f579237800a7abb6..7d956987442b2bde7cd9cfa32c7ebf52226a9a54 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/component.jsx @@ -62,6 +62,7 @@ class AudioControls extends Component { listenOnly, intl, shortcuts, + currentUser, } = this.props; let joinIcon = 'audio_off'; @@ -75,25 +76,23 @@ class AudioControls extends Component { return ( <span className={styles.container}> - {showMute - ? ( - <Button - className={cx(styles.button, !talking || styles.glow, !muted || styles.btn)} - onClick={handleToggleMuteMicrophone} - disabled={disable} - hideLabel - label={muted ? intl.formatMessage(intlMessages.unmuteAudio) - : intl.formatMessage(intlMessages.muteAudio)} - aria-label={muted ? intl.formatMessage(intlMessages.unmuteAudio) - : intl.formatMessage(intlMessages.muteAudio)} - color={!muted ? 'primary' : 'default'} - ghost={muted} - icon={muted ? 'mute' : 'unmute'} - size="lg" - circle - accessKey={shortcuts.toggleMute} - /> - ) : null} + {showMute && currentUser.isVoiceUser ? + <Button + className={cx(styles.button, !talking || styles.glow, !muted || styles.btn)} + onClick={handleToggleMuteMicrophone} + disabled={disable} + hideLabel + label={muted ? intl.formatMessage(intlMessages.unmuteAudio) + : intl.formatMessage(intlMessages.muteAudio)} + aria-label={muted ? intl.formatMessage(intlMessages.unmuteAudio) + : intl.formatMessage(intlMessages.muteAudio)} + color={!muted ? 'primary' : 'default'} + ghost={muted} + icon={muted ? 'mute' : 'unmute'} + size="lg" + circle + accessKey={shortcuts.toggleMute} + /> : null} <Button className={cx(styles.button, inAudio || styles.btn)} onClick={inAudio ? handleLeaveAudio : handleJoinAudio} diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx index b4666a77912f3e32b15784eae52aa09535f70742..aa795e0ffd7879424d681e902ec435c27e24409a 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx @@ -39,6 +39,7 @@ export default withModalMounter(withTracker(({ mountModal }) => ({ listenOnly: Service.isConnected() && Service.isListenOnly(), disable: Service.isConnecting() || Service.isHangingUp(), talking: Service.isTalking() && !Service.isMuted(), + currentUser: Service.currentUser(), handleToggleMuteMicrophone: () => Service.toggleMuteMicrophone(), handleJoinAudio: () => (Service.isConnected() ? Service.joinListenOnly() : mountModal(<AudioModalContainer />)), handleLeaveAudio: () => Service.exitAudio(), diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js index 027b83f8bd7d25bda2a8b37ca0f9e4d4cc58959f..2338fec7a1f2681732cbaea398fc08ae89eacb45 100755 --- a/bigbluebutton-html5/imports/ui/components/audio/service.js +++ b/bigbluebutton-html5/imports/ui/components/audio/service.js @@ -41,6 +41,8 @@ const audioLocked = () => { return audioLock && User.isLocked; }; +const currentUser = () => mapUser(Users.findOne({ intId: Auth.userID })); + export default { init, exitAudio: () => AudioManager.exitAudio(), @@ -65,4 +67,5 @@ export default { error: () => AudioManager.error, isUserModerator: () => Users.findOne({ userId: Auth.userID }).moderator, audioLocked, + currentUser, }; diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx index 9608eff88588820680fb3fcecaa5abcd1c94ecb7..543d0d52d4cd21662b36252cbc25026e6fbd11cc 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx @@ -98,7 +98,11 @@ class BreakoutRoom extends Component { const hasUser = breakoutRoomUser(breakoutId); if (!hasUser && !waiting) { this.setState( - { waiting: true, requestedBreakoutId: breakoutId }, + { + waiting: true, + requestedBreakoutId: breakoutId, + generated: false, + }, () => requestJoinURL(breakoutId), ); } @@ -196,7 +200,7 @@ class BreakoutRoom extends Component { {intl.formatMessage(intlMessages.breakoutRoom, breakout.sequence.toString())} <span className={styles.usersAssignedNumberLabel}> ( - {breakout.users.length} + {new Set(breakout.users.map(user => user.intId)).size} ) </span> </span> diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/modal/component.jsx b/bigbluebutton-html5/imports/ui/components/external-video-player/modal/component.jsx index ea6f8cc4056d46428e1257baa46cd3cb0422886d..51d6bd93261fe8703e38c56f724b95b983593428 100644 --- a/bigbluebutton-html5/imports/ui/components/external-video-player/modal/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/external-video-player/modal/component.jsx @@ -26,6 +26,10 @@ const intlMessages = defineMessages({ id: 'app.externalVideo.input', description: 'Video URL', }, + urlInput: { + id: 'app.externalVideo.urlInput', + description: 'URL input field placeholder', + }, title: { id: 'app.externalVideo.title', description: 'Modal title', @@ -34,6 +38,10 @@ const intlMessages = defineMessages({ id: 'app.externalVideo.close', description: 'Close', }, + note: { + id: 'app.externalVideo.noteLabel', + description: 'provides hint about Shared YouTube videos', + }, }); class ExternalVideoModal extends Component { @@ -124,8 +132,12 @@ class ExternalVideoModal extends Component { onChange={this.updateVideoUrlHandler} name="video-modal-input" value={url} + placeholder={intl.formatMessage(intlMessages.urlInput)} /> </label> + <div className={styles.youtubeNote}> + {intl.formatMessage(intlMessages.note)} + </div> </div> <div className={styles.content}> diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/modal/styles.scss b/bigbluebutton-html5/imports/ui/components/external-video-player/modal/styles.scss index ea4aabf9561e33866e1183e3f07cf0334b8d54ea..f13935f0585b5a3a2398450f9358bc0bbca2d478 100755 --- a/bigbluebutton-html5/imports/ui/components/external-video-player/modal/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/external-video-player/modal/styles.scss @@ -1,5 +1,6 @@ @import "/imports/ui/stylesheets/variables/_all"; @import "/imports/ui/components/modal/simple/styles"; +@import "/imports/ui/stylesheets/mixins/focus"; .header { margin: 0; @@ -114,32 +115,30 @@ } .videoUrl { + margin: 0 var(--border-size) 0 var(--border-size); + label { display: block; } input { + @include inputFocus(var(--color-blue-light)); display: block; margin: 10px 0 10px 0; padding: 0.4em; - background-color: #F1F8FF; + color: var(--color-text); line-height: 2rem; width: 100%; font-family: inherit; font-weight: inherit; - border: none; - border-radius: 0.4rem; + border: 1px solid var(--color-gray-lighter); + border-radius: var(--border-radius); [style~="--enableAnimation:1;"] & { transition: box-shadow .2s; } } - input:focus { - outline: none; - box-shadow: 0.2rem 0.8rem 1.6rem 600; - } - span { font-weight: 600; } @@ -154,3 +153,9 @@ } } +.youtubeNote { + color: var(--color-gray); + font-size: var(--font-size-small); + font-style: italic; + padding-top: var(--sm-padding-x); +} diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx index d7d28ba5c665ee0f02de7e5f8dadf64571d39069..13349df55566569977a0d942efe28fabb3c87bee 100755 --- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx @@ -59,6 +59,26 @@ const intlMessage = defineMessages({ id: 'app.feedback.sendFeedbackDesc', description: 'adds context to send feedback option', }, + duplicate_user_in_meeting_eject_reason: { + id: 'app.meeting.logout.duplicateUserEjectReason', + description: 'message for duplicate users', + }, + not_enough_permission_eject_reason: { + id: 'app.meeting.logout.permissionEjectReason', + description: 'message for whom was kicked by doing something without permission', + }, + user_requested_eject_reason: { + id: 'app.meeting.logout.ejectedFromMeeting', + description: 'message when the user is removed by someone', + }, + validate_token_failed_eject_reason: { + id: 'app.meeting.logout.validateTokenFailedEjectReason', + description: 'invalid auth token', + }, + user_inactivity_eject_reason: { + id: 'app.meeting.logout.userInactivityEjectReason', + description: 'message for whom was kicked by inactivity', + }, }); const propTypes = { diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx index c6892e84f4617966d55d7caba28900eb73ef1386..57d08d3e7051955731114121939356abde3e2d85 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/component.jsx @@ -161,12 +161,19 @@ class NavBar extends PureComponent { } breakouts.forEach((breakout) => { + const userOnMeeting = breakout.users.filter(u => u.userId === Auth.userID).length; + if (breakout.freeJoin + && !didSendBreakoutInvite + && !userOnMeeting + && !isBreakoutRoom) { + this.inviteUserToBreakout(breakout); + this.setState({ didSendBreakoutInvite: true }); + } + if (!breakout.users) { return; } - const userOnMeeting = breakout.users.filter(u => u.userId === Auth.userID).length; - if (!userOnMeeting) return; if ((!didSendBreakoutInvite && !isBreakoutRoom) ) { diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx index ff61bf45c0e0a94a0c737c0882ae753e13e35a2e..2ef6b417ed3d4aa271ee17a7c9641299f2360377 100755 --- a/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/nav-bar/settings-dropdown/component.jsx @@ -163,8 +163,8 @@ class SettingsDropdown extends PureComponent { } leaveSession() { + document.dispatchEvent(new Event('exitVideo')); const { mountModal } = this.props; - const LOGOUT_CODE = '430'; // we don't check askForFeedbackOnLogout here, // it is checked in meeting-ended component diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx index 32ca1461b9aa80ed21c9d44ba5450de284a8fcf5..29cb7a69c9195d67e2e29b02128ed581d0c7bf37 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx @@ -47,6 +47,18 @@ class PresentationArea extends Component { this.fitToWidthHandler = this.fitToWidthHandler.bind(this); } + componentDidUpdate(prevProps, prevState) { + if (prevState.fitToWidth) { + // When presenter is changed or slide changed we reset fitToWidth + if ((prevProps.userIsPresenter && !this.props.userIsPresenter) || + (prevProps.currentSlide.id !== this.props.currentSlide.id)) { + this.setState({ + fitToWidth: false, + }); + } + } + } + componentDidMount() { // adding an event listener to scale the whiteboard on 'resize' events sent by chat/userlist etc window.addEventListener('resize', () => { @@ -129,7 +141,7 @@ class PresentationArea extends Component { } calculateSize() { - const { presentationHeight, presentationWidth } = this.state; + const { presentationHeight, presentationWidth, fitToWidth } = this.state; const { currentSlide } = this.props; const originalWidth = currentSlide.calculatedData.width; @@ -138,25 +150,30 @@ class PresentationArea extends Component { let adjustedWidth; let adjustedHeight; - // Slide has a portrait orientation - if (originalWidth <= originalHeight) { - adjustedWidth = (presentationHeight * originalWidth) / originalHeight; - if (presentationWidth < adjustedWidth) { - adjustedHeight = (presentationHeight * presentationWidth) / adjustedWidth; - adjustedWidth = presentationWidth; - } else { - adjustedHeight = presentationHeight; - } - + if (!fitToWidth) { + // Slide has a portrait orientation + if (originalWidth <= originalHeight) { + adjustedWidth = (presentationHeight * originalWidth) / originalHeight; + if (presentationWidth < adjustedWidth) { + adjustedHeight = (presentationHeight * presentationWidth) / adjustedWidth; + adjustedWidth = presentationWidth; + } else { + adjustedHeight = presentationHeight; + } // Slide has a landscape orientation - } else { - adjustedHeight = (presentationWidth * originalHeight) / originalWidth; - if (presentationHeight < adjustedHeight) { - adjustedWidth = (presentationWidth * presentationHeight) / adjustedHeight; - adjustedHeight = presentationHeight; } else { - adjustedWidth = presentationWidth; + adjustedHeight = (presentationWidth * originalHeight) / originalWidth; + if (presentationHeight < adjustedHeight) { + adjustedWidth = (presentationWidth * presentationHeight) / adjustedHeight; + adjustedHeight = presentationHeight; + } else { + adjustedWidth = presentationWidth; + } } + } else { + adjustedWidth = presentationWidth; + adjustedHeight = (adjustedWidth * originalHeight) / originalWidth; + if (adjustedHeight > presentationHeight) adjustedHeight = presentationHeight; } return { width: adjustedWidth, @@ -164,6 +181,24 @@ class PresentationArea extends Component { }; } + // TODO: This could be replaced if we synchronize the fit-to-width state between users + checkFitToWidth() { + const { userIsPresenter, currentSlide } = this.props; + const { fitToWidth } = this.state; + if (userIsPresenter) { + return fitToWidth; + } else { + const { width, height, viewBoxWidth, viewBoxHeight } = currentSlide.calculatedData; + const slideSizeRatio = width / height; + const viewBoxSizeRatio = viewBoxWidth / viewBoxHeight; + if (slideSizeRatio !== viewBoxSizeRatio) { + return true; + } else { + return false; + } + } + } + zoomChanger(incomingZoom) { const { zoom } = this.state; let newZoom = incomingZoom; @@ -284,7 +319,7 @@ class PresentationArea extends Component { // renders the whole presentation area renderPresentationArea() { - const { fitToWidth } = this.state; + const { presentationWidth } = this.state; const { podId, currentSlide } = this.props; if (!this.isPresentationAccessible()) return null; @@ -306,7 +341,7 @@ class PresentationArea extends Component { imageUri, } = slideObj.calculatedData; - const svgAreaDimensions = fitToWidth + const svgAreaDimensions = this.checkFitToWidth() ? { position: 'absolute', width: 'inherit', @@ -351,7 +386,7 @@ class PresentationArea extends Component { version="1.1" xmlns="http://www.w3.org/2000/svg" className={styles.svgStyles} - style={fitToWidth + style={this.checkFitToWidth() ? { position: 'absolute', } @@ -380,10 +415,9 @@ class PresentationArea extends Component { podId={podId} whiteboardId={slideObj.id} widthRatio={slideObj.widthRatio} - physicalWidthRatio={adjustedSizes.width / width} + physicalWidthRatio={this.checkFitToWidth() ? (presentationWidth / width) : (adjustedSizes.width / width)} slideWidth={width} slideHeight={height} - radius={fitToWidth ? 2 : 5} /> </g> {this.renderOverlays(slideObj, adjustedSizes)} diff --git a/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx index ada7003b6f496ee917c7f809d4de1b69e4566f83..b64f67cd72a0c863c9beba78bda525252eea268c 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/cursor/component.jsx @@ -40,6 +40,8 @@ export default class Cursor extends Component { } static getScaledSizes(props) { + // TODO: This might need to change for the use case of fit-to-width portrait + // slides in non-presenter view. Some elements are still shrinking. const scaleFactor = props.widthRatio / 100 / props.physicalWidthRatio; return { diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-close-button/styles.scss b/bigbluebutton-html5/imports/ui/components/presentation/presentation-close-button/styles.scss index 2c3384c4cd6fd8ddf103a4a3068f6a820f2779e3..9764bcfb537e14ccb12daef35f191952056d8561 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-close-button/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-close-button/styles.scss @@ -1,5 +1,6 @@ .button { + z-index: 1; position: absolute; top: 0; right: 0; diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx index f6fe1a45c806cfa6b8299ff3a4d49bf65a4d90c5..d95b52b5c30f3a6d6d91c64f81b01cf75ba6b35b 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx @@ -70,8 +70,6 @@ export default class PresentationOverlay extends Component { presentationSize, } = props; - this.fitToPage = false; - this.viewportW = slideWidth; this.viewportH = slideHeight; @@ -107,6 +105,7 @@ export default class PresentationOverlay extends Component { const zoomPercentage = (Math.round((100 / realZoom) * 100)); const roundedUpToFive = Math.round(zoomPercentage / 5) * 5; zoomChanger(roundedUpToFive); + this.doZoomCall(HUNDRED_PERCENT, 0, 0); } componentDidUpdate(prevProps) { @@ -117,6 +116,7 @@ export default class PresentationOverlay extends Component { presentationSize, slideHeight, slideWidth, + fitToWidth, } = this.props; const isDifferent = zoom !== this.state.zoom && !touchZoom; const moveSLide = ((delta.x !== prevProps.delta.x) @@ -134,21 +134,28 @@ export default class PresentationOverlay extends Component { this.toolbarZoom(); } - if (!prevProps.fitToWidth && this.props.fitToWidth) { - this.parentH = presentationSize.presentationHeight; - this.parentW = presentationSize.presentationWidth; - this.viewportH = this.parentH; - this.viewportW = this.parentW; - this.doZoomCall(HUNDRED_PERCENT, 0, 0); - } - - if (!this.props.fitToWidth && prevProps.fitToWidth) { + if (fitToWidth) { + if (!prevProps.fitToWidth || this.checkResize(prevProps.presentationSize)) { + this.parentH = presentationSize.presentationHeight; + this.parentW = presentationSize.presentationWidth; + this.viewportH = this.parentH; + this.viewportW = this.parentW; + this.doZoomCall(HUNDRED_PERCENT, 0, 0); + } + } else if (prevProps.fitToWidth) { this.viewportH = slideHeight; this.viewportW = slideWidth; this.doZoomCall(HUNDRED_PERCENT, 0, 0); } } + checkResize(prevPresentationSize) { + const { presentationSize } = this.props; + const heightChanged = prevPresentationSize.presentationHeight !== presentationSize.presentationHeight; + const widthChanged = prevPresentationSize.presentationWidth !== presentationSize.presentationWidth; + return heightChanged || widthChanged; + } + onZoom(zoomValue, mouseX, mouseY) { let absXcoordInPage = (Math.abs(this.calcPageX) * MYSTERY_NUM) + mouseX; let absYcoordInPage = (Math.abs(this.calcPageY) * MYSTERY_NUM) + mouseY; @@ -156,15 +163,22 @@ export default class PresentationOverlay extends Component { const relXcoordInPage = absXcoordInPage / this.calcPageW; const relYcoordInPage = absYcoordInPage / this.calcPageH; - if (this.isPortraitDoc() && this.fitToPage) { - this.calcPageH = (this.viewportH * zoomValue) / HUNDRED_PERCENT; - this.calcPageW = (this.pageOrigW / this.pageOrigH) * this.calcPageH; - } else if (!this.isPortraitDoc() && this.fitToPage) { - this.calcPageW = (this.viewportW * zoomValue) / HUNDRED_PERCENT; - this.calcPageH = (this.viewportH * zoomValue) / HUNDRED_PERCENT; + if (this.isPortraitDoc()) { + if (this.props.fitToWidth) { + this.calcPageW = (this.viewportW * zoomValue) / HUNDRED_PERCENT; + this.calcPageH = (this.calcPageW / this.pageOrigW) * this.pageOrigH; + } else { + this.calcPageH = (this.viewportH * zoomValue) / HUNDRED_PERCENT; + this.calcPageW = (this.pageOrigW / this.pageOrigH) * this.calcPageH; + } } else { - this.calcPageW = (this.viewportW * zoomValue) / HUNDRED_PERCENT; - this.calcPageH = (this.calcPageW / this.pageOrigW) * this.pageOrigH; + if (this.props.fitToWidth) { + this.calcPageW = (this.viewportW * zoomValue) / HUNDRED_PERCENT; + this.calcPageH = (this.calcPageW / this.pageOrigW) * this.pageOrigH; + } else { + this.calcPageW = (this.viewportW * zoomValue) / HUNDRED_PERCENT; + this.calcPageH = (this.viewportH * zoomValue) / HUNDRED_PERCENT; + } } absXcoordInPage = relXcoordInPage * this.calcPageW; @@ -181,6 +195,8 @@ export default class PresentationOverlay extends Component { getTransformedSvgPoint(clientX, clientY) { const svgObject = this.props.getSvgRef(); + // If svgObject is not ready, return origin + if (!svgObject) return { x: 0, y: 0 }; const screenPoint = svgObject.createSVGPoint(); screenPoint.x = clientX; screenPoint.y = clientY; diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx index c65fe63bc7114eea3ad192cf6b1b122ae3e1565d..bbd7b03d3123134758de5c27309fd1003f6f1a77 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/component.jsx @@ -23,7 +23,7 @@ const propTypes = { fileSizeMax: PropTypes.number.isRequired, handleSave: PropTypes.func.isRequired, dispatchTogglePresentationDownloadable: PropTypes.func.isRequired, - fileValidMimeTypes: PropTypes.arrayOf(PropTypes.string).isRequired, + fileValidMimeTypes: PropTypes.arrayOf(PropTypes.object).isRequired, presentations: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired, filename: PropTypes.string.isRequired, @@ -268,8 +268,9 @@ class PresentationUploader extends Component { handleFiledrop(files, files2) { const { fileValidMimeTypes, intl } = this.props; + const mimeTypes = fileValidMimeTypes.map(fileValid => fileValid.mime); const [accepted, rejected] = _.partition(files - .concat(files2), f => fileValidMimeTypes.includes(f.type)); + .concat(files2), f => mimeTypes.includes(f.type)); const presentationsToUpload = accepted.map((file) => { const id = _.uniqueId(file.name); @@ -605,7 +606,7 @@ class PresentationUploader extends Component { className={styles.dropzone} activeClassName={styles.dropzoneActive} rejectClassName={styles.dropzoneReject} - accept={isMobileBrowser ? '' : fileValidMimeTypes.join()} + accept={isMobileBrowser ? '' : fileValidMimeTypes.map(fileValid => fileValid.extension)} minSize={fileSizeMin} maxSize={fileSizeMax} disablepreview="true" diff --git a/bigbluebutton-html5/imports/ui/components/recording/component.jsx b/bigbluebutton-html5/imports/ui/components/recording/component.jsx index 72266f5a935769c4144d89698ac18699f6daddd9..f7b26ae8caa9d0fad9f1ca1d80129de5b8eeb57d 100755 --- a/bigbluebutton-html5/imports/ui/components/recording/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/recording/component.jsx @@ -14,6 +14,10 @@ const intlMessages = defineMessages({ id: 'app.recording.stopTitle', description: 'stop recording title', }, + resumeTitle: { + id: 'app.recording.resumeTitle', + description: 'resume recording title', + }, startDescription: { id: 'app.recording.startDescription', description: 'start recording description', @@ -36,11 +40,13 @@ const propTypes = { intl: intlShape.isRequired, closeModal: PropTypes.func.isRequired, toggleRecording: PropTypes.func.isRequired, + recordingTime: PropTypes.number, recordingStatus: PropTypes.bool, amIModerator: PropTypes.bool, }; const defaultProps = { + recordingTime: -1, recordingStatus: false, amIModerator: false, }; @@ -50,11 +56,21 @@ class RecordingComponent extends React.PureComponent { const { intl, recordingStatus, + recordingTime, amIModerator, closeModal, toggleRecording, } = this.props; + let title; + + if (!recordingStatus) { + title = recordingTime >= 0 ? intl.formatMessage(intlMessages.resumeTitle) + : intl.formatMessage(intlMessages.startTitle); + } else { + title = intl.formatMessage(intlMessages.stopTitle); + } + if (!amIModerator) return null; return ( <Modal @@ -66,11 +82,7 @@ class RecordingComponent extends React.PureComponent { <div className={styles.container}> <div className={styles.header}> <div className={styles.title}> - { - intl.formatMessage(!recordingStatus - ? intlMessages.startTitle - : intlMessages.stopTitle) - } + {title} </div> </div> <div className={styles.description}> diff --git a/bigbluebutton-html5/imports/ui/components/recording/container.jsx b/bigbluebutton-html5/imports/ui/components/recording/container.jsx index 72815e90b48c75dd0b6de05b8b33a670ddd54460..4c212cfc179467440f56df4bcb233c49bd45385c 100755 --- a/bigbluebutton-html5/imports/ui/components/recording/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/recording/container.jsx @@ -8,16 +8,19 @@ import RecordingComponent from './component'; const RecordingContainer = props => <RecordingComponent {...props} />; -export default withModalMounter(withTracker(({ mountModal }) => ({ - closeModal() { - mountModal(null); - }, +export default withModalMounter(withTracker(({ mountModal }) => { + const { recording, time } = Meetings.findOne({ meetingId: Auth.meetingID }).recordProp; - toggleRecording: () => { - makeCall('toggleRecording'); - mountModal(null); - }, + return ({ + closeModal: () => mountModal(null), - recordingStatus: (Meetings.findOne({ meetingId: Auth.meetingID }).recordProp.recording), + toggleRecording: () => { + makeCall('toggleRecording'); + mountModal(null); + }, -}))(RecordingContainer)); + recordingStatus: recording, + recordingTime: time, + + }); +})(RecordingContainer)); diff --git a/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx b/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx index 7fecf629a68c86ba9f464930eb567775fccd7ca5..52f712f7716d393b0949f9624bd6c519711b321c 100644 --- a/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx @@ -154,7 +154,7 @@ class VideoPreview extends Component { video: VIDEO_CONSTRAINTS, }; - navigator.mediaDevices.enumerateDevices().then((devices) => { + navigator.mediaDevices.enumerateDevices().then(async (devices) => { const { isInitialDeviceSet } = this.state; const webcams = []; @@ -175,9 +175,10 @@ class VideoPreview extends Component { constraints.video.deviceId = { exact: this.state.webcamDeviceId }; - const iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0; - - if (iOS) { + try { + await navigator.mediaDevices.getUserMedia(constraints); + } catch (exception) { + logger.info({ logCode: 'insufficient_constraints' }, 'No webcam found for constraint values, increasing constraints.', exception); constraints.video.width = { max: 640 }; constraints.video.height = { max: 480 }; } diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx index 58afa06db3260f164ead950ea86555b5bf7a20f4..1fa16e4871a70c18351238a901f9b687916233e0 100644 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx @@ -169,6 +169,7 @@ class VideoList extends Component { })} > <VideoListItem + numOfUsers={users.length} user={user} actions={actions} onMount={(videoRef) => { diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss index 4e0943178bc91699f98a9f00789590b230de088a..ab0b95117713bce2e94e8cb27ed7ca77e47a3cc6 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/styles.scss @@ -144,12 +144,16 @@ .dropdownTrigger, .userName { @extend %text-elipsis; position: relative; - background-color: rgba(var(--color-gray), .5); + background-color: var(--color-black); + opacity: .5; color: var(--color-white); font-size: 80%; border-radius: 15px; padding: 0 1rem 0 .5rem !important; +} +.noMenu { + padding: 0 .5rem 0 .5rem !important; } .dropdownTrigger { diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx index 183b70fde313a71f3ea7fa3b9dbbca8864fa0eea..5b64b591fc8b10868e8cce746fe6bcdc72793b68 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/video-list/video-list-item/component.jsx @@ -1,8 +1,9 @@ import React, { Component } from 'react'; import browser from 'browser-detect'; import { Meteor } from 'meteor/meteor'; +import PropTypes from 'prop-types'; import cx from 'classnames'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, injectIntl, intlShape } from 'react-intl'; import Dropdown from '/imports/ui/components/dropdown/component'; import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component'; import DropdownContent from '/imports/ui/components/dropdown/content/component'; @@ -121,7 +122,7 @@ class VideoListItem extends Component { render() { const { showStats, stats } = this.state; - const { user } = this.props; + const { user, numOfUsers } = this.props; const availableActions = this.getAvailableActions(); const enableVideoMenu = Meteor.settings.public.kurento.enableVideoMenu || false; @@ -162,7 +163,13 @@ class VideoListItem extends Component { <div className={isFirefox ? styles.dropdownFireFox : styles.dropdown} > - <span className={styles.userName}>{user.name}</span> + <span className={cx({ + [styles.userName]: true, + [styles.noMenu]: numOfUsers < 3, + })} + > + {user.name} + </span> </div> ) } @@ -177,3 +184,20 @@ class VideoListItem extends Component { } export default injectIntl(VideoListItem); + +VideoListItem.defaultProps = { + numOfUsers: 0, +}; + +VideoListItem.propTypes = { + intl: intlShape.isRequired, + enableVideoStats: PropTypes.bool.isRequired, + actions: PropTypes.arrayOf(PropTypes.func).isRequired, + user: PropTypes.objectOf(PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.number, + PropTypes.object, + PropTypes.string, + ])).isRequired, + numOfUsers: PropTypes.number, +}; diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index e20b176fc56d9e9b8d677c98220f3ef89aa527c8..b5b9219d0cd9d594c0a3b21eafe49b2c67245f97 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -148,24 +148,40 @@ public: uploadSizeMin: 0 uploadSizeMax: 50000000 uploadValidMimeTypes: - - application/vnd.ms-excel - - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - - application/msword - - application/vnd.openxmlformats-officedocument.wordprocessingml.document - - application/vnd.ms-powerpoint - - application/vnd.openxmlformats-officedocument.presentationml.presentation - - application/vnd.oasis.opendocument.text - - application/rtf - - text/plain - - application/vnd.oasis.opendocument.spreadsheet - - application/vnd.oasis.opendocument.presentation - - application/vnd.oasis.opendocument.text - - application/vnd.oasis.opendocument.graphics - - application/vnd.oasis.opendocument.chart - - application/vnd.oasis.opendocument.image - - application/pdf - - image/jpeg - - image/png + - extension: .pdf + mime: application/pdf + - extension: .doc + mime: application/msword + - extension: .docx + mime: application/vnd.openxmlformats-officedocument.wordprocessingml.document + - extension: .xls + mime: application/vnd.ms-excel + - extension: .xlsx + mime: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - extension: .ppt + mime: application/vnd.ms-powerpoint + - extension: .pptx + mime: application/vnd.openxmlformats-officedocument.presentationml.presentation + - extension: .txt + mime: text/plain + - extension: .rtf + mime: application/rtf + - extension: .odt + mime: application/vnd.oasis.opendocument.text + - extension: .ods + mime: application/vnd.oasis.opendocument.spreadsheet + - extension: .odp + mime: application/vnd.oasis.opendocument.presentation + - extension: .odg + mime: application/vnd.oasis.opendocument.graphics + - extension: .odc + mime: application/vnd.oasis.opendocument.chart + - extension: .odi + mime: application/vnd.oasis.opendocument.image + - extension: .jpg + mime: image/jpeg + - extension: .png + mime: image/png user: role_moderator: MODERATOR role_viewer: VIEWER diff --git a/bigbluebutton-html5/private/locales/cs_CZ.json b/bigbluebutton-html5/private/locales/cs_CZ.json index 67cad8e46e81445189ce0bb9e75ad81a1146358e..7e69fb024a27434990fc942f5177646cc165c781 100644 --- a/bigbluebutton-html5/private/locales/cs_CZ.json +++ b/bigbluebutton-html5/private/locales/cs_CZ.json @@ -393,7 +393,6 @@ "app.lock-viewers.PrivateChatLable": "Soukromý chat", "app.lock-viewers.Layout": "Rozloženà obrazovky", "app.recording.startTitle": "Zahájit nahrávánÃ", - "app.recording.stopTitle": "Zastavit nahrávánÃ", "app.recording.startDescription": "(Můžete využÃt nahrávacà tlaÄÃtko pozdÄ›ji pro pozastavenà nahrávánÃ.)", "app.recording.stopDescription": "Jste si jist, že chcete pozastavit nahrávánÃ? (PozdÄ›ji můžete pokraÄovat v nahrávánà opakovaným stiskem nahrávacÃho tlaÄÃtka.)", "app.videoPreview.cameraLabel": "Kamera", @@ -509,7 +508,6 @@ "app.createBreakoutRoom.doneLabel": "Hotovo", "app.createBreakoutRoom.nextLabel": "DalÅ¡Ã", "app.createBreakoutRoom.minusRoomTime": "Prodloužit Äas vedlejÅ¡Ã mÃstnosti", - "app.createBreakoutRoom.addRoomTime": "Zkrátit Äas vedlejÅ¡Ã mÃstnosti", "app.createBreakoutRoom.addParticipantLabel": "+ PÅ™idat posluchaÄe", "app.createBreakoutRoom.freeJoin": "Dovolit posluchaÄům, aby si sami vybrali, do které vedlejÅ¡Ã mÃstnosti se pÅ™ipojÃ", "app.createBreakoutRoom.leastOneWarnBreakout": "Do vedlejÅ¡Ã mÃstnosti musÃte pÅ™iÅ™adit alespoň jednoho posluchaÄe", diff --git a/bigbluebutton-html5/private/locales/de.json b/bigbluebutton-html5/private/locales/de.json index 5b91671f0e1be375b0cb9211f4cb84a1c936ffcd..17d994a83f4b3cf18b0dd9263e2e78061f8b8297 100644 --- a/bigbluebutton-html5/private/locales/de.json +++ b/bigbluebutton-html5/private/locales/de.json @@ -21,6 +21,9 @@ "app.note.title": "Geteilte Notizen", "app.note.label": "Notiz", "app.note.hideNoteLabel": "Notiz verbergen", + "app.user.activityCheck": "Nutzeraktivitätsprüfung", + "app.user.activityCheck.label": "Prüfen ob Nutzer noch in der Konferenz ist ({0})", + "app.user.activityCheck.check": "Prüfen", "app.note.tipLabel": "Drücken Sie Esc um die Editorwerkzeugliste auszuwählen", "app.userList.usersTitle": "Teilnehmer", "app.userList.participantsTitle": "Teilnehmer", @@ -72,6 +75,7 @@ "app.presentation.startSlideContent": "Beginn des Folieninhalts", "app.presentation.endSlideContent": "Ende des Folieninhalts", "app.presentation.emptySlideContent": "Kein Inhalt für aktuelle Folie", + "app.presentation.presentationToolbar.selectLabel": "Folie auswählen", "app.presentation.presentationToolbar.prevSlideLabel": "Vorherige Folie", "app.presentation.presentationToolbar.prevSlideDesc": "Präsentation zur vorherigen Folie wechseln", "app.presentation.presentationToolbar.nextSlideLabel": "Nächste Folie", @@ -91,6 +95,7 @@ "app.presentation.presentationToolbar.zoomReset": "Zoom zurücksetzen", "app.presentation.presentationToolbar.zoomIndicator": "Aktuelle Zoom-Stufe", "app.presentation.presentationToolbar.fitToWidth": "An Breite anpassen", + "app.presentation.presentationToolbar.fitToPage": "An Seite anpassen", "app.presentation.presentationToolbar.goToSlide": "Folie {0}", "app.presentationUploder.title": "Präsentation", "app.presentationUploder.message": "Als Präsentator in BigBlueButton haben sie die Möglichkeit Office-Dokumente oder PDF-Dateien hochzuladen. PDF-Dateien haben dabei die bessere Qualität.", @@ -132,6 +137,8 @@ "app.poll.closeLabel": "Schließen", "app.poll.ariaInputCount": "Eingabe {0} von {1}", "app.poll.customPlaceholder": "Umfrageoption hinzufügen", + "app.poll.noPresentationSelected": "Keine Präsentation ausgewählt! Bitte eine auswählen.", + "app.poll.clickHereToSelect": "Zum Auswählen hier klicken", "app.poll.t": "Wahr", "app.poll.f": "Falsch", "app.poll.tf": "Richtig / Falsch", @@ -376,6 +383,16 @@ "app.error.400": "Ungültige Anfrage", "app.error.leaveLabel": "Erneut einloggen", "app.guest.waiting": "Warte auf Erlaubnis zur Konferenzteilnahme", + "app.userList.guest.waitingUsers": "Wartende Teilnehmer", + "app.userList.guest.waitingUsersTitle": "Teilnehmerverwaltung", + "app.userList.guest.optionTitle": "Unbearbeitete Teilnehmer überprüfen", + "app.userList.guest.allowAllAuthenticated": "Alle Autorisierten erlauben", + "app.userList.guest.allowAllGuests": "Alle Gäste erlauben", + "app.userList.guest.allowEveryone": "Alle erlauben", + "app.userList.guest.denyEveryone": "Alle verweigern", + "app.userList.guest.pendingUsers": "{0} unbearbeitete Teilnehmer", + "app.userList.guest.pendingGuestUsers": "{0} unbearbeitete Gäste", + "app.userList.guest.pendingGuestAlert": "Ist der Konferenz beigetreten und wartet auf Ihre Teilnahmeerlaubnis", "app.toast.breakoutRoomEnded": "Breakout-Raum wurde beendet. Bitte treten Sie der Audiokonferenz erneut bei.", "app.toast.chat.public": "Neue öffentliche Chatnachricht", "app.toast.chat.private": "Neue private Chatnachricht", @@ -400,15 +417,15 @@ "app.lock-viewers.title": "Zuschauerrechte einschränken", "app.lock-viewers.description": "Diese Optionen ermöglichen es, bestimmte Funktionen für Zuschauer einzuschränken, wie z.B. die Nutzung des privaten Chats. (Diese Einschränkungen gelten nicht für Moderatoren)", "app.lock-viewers.featuresLable": "Funktion", - "app.lock-viewers.lockStatusLabel": "Sperrstatus", + "app.lock-viewers.lockStatusLabel": "Gesperrt", "app.lock-viewers.webcamLabel": "Webcam", "app.lock-viewers.otherViewersWebcamLabel": "Nur Moderatoren sehen Webcams", - "app.lock-viewers.microphoneLable": "Mit Mikrofon", + "app.lock-viewers.microphoneLable": "Mikrofon", "app.lock-viewers.PublicChatLabel": "Öffentlicher Chat", "app.lock-viewers.PrivateChatLable": "Privater Chat", "app.lock-viewers.Layout": "Layout", "app.recording.startTitle": "Aufzeichnung starten", - "app.recording.stopTitle": "Aufzeichnung stoppen", + "app.recording.stopTitle": "Aufzeichnung pausieren", "app.recording.startDescription": "(Sie können den Aufnahmeknopf erneut drücken, um die Aufzeichnung zu pausieren)", "app.recording.stopDescription": "Sind Sie sicher, dass die die Aufnahme pausieren wollen? (Sie können die Aufnahme fortsetzen, in dem Sie auf den Aufnahmeknopf drücken)", "app.videoPreview.cameraLabel": "Kamera", @@ -524,7 +541,7 @@ "app.createBreakoutRoom.doneLabel": "Fertig", "app.createBreakoutRoom.nextLabel": "Nächster", "app.createBreakoutRoom.minusRoomTime": "Breakout-Raum Zeit erhöhen", - "app.createBreakoutRoom.addRoomTime": "Breakout-Raum Zeit reduzieren", + "app.createBreakoutRoom.addRoomTime": "Breakout Raum Zeit verringern", "app.createBreakoutRoom.addParticipantLabel": "+ Teilnehmer hinzufügen", "app.createBreakoutRoom.freeJoin": "Den Teilnehmern erlauben, sich selber einen Breakout-Raum auszusuchen.", "app.createBreakoutRoom.leastOneWarnBreakout": "Jedem Breakout-Raum muss wenigstens ein Teilnehmer zugeordnet sein.", diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index 977b9c23b38331392a6476b1de7f7c29c98b5941..977b389855ed9f89454e55b9c13442ef686824ea 100755 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -98,7 +98,7 @@ "app.presentation.presentationToolbar.fitToPage": "Fit to page", "app.presentation.presentationToolbar.goToSlide": "Slide {0}", "app.presentationUploder.title": "Presentation", - "app.presentationUploder.message": "As a presenter in BigBlueButton, you have the ability of uploading any office document or PDF file. We recommend PDF file for best results.", + "app.presentationUploder.message": "As a presenter you have the ability of uploading any office document or PDF file. We recommend PDF file for best results.", "app.presentationUploder.confirmLabel": "Start", "app.presentationUploder.confirmDesc": "Save your changes and start the presentation", "app.presentationUploder.dismissLabel": "Cancel", @@ -375,6 +375,11 @@ "app.audio.permissionsOverlay.hint": "We need you to allow us to use your media devices in order to join you to the voice conference :)", "app.error.removed": "You have been removed from the conference", "app.error.meeting.ended": "You have logged out of the conference", + "app.meeting.logout.duplicateUserEjectReason": "Duplicate user trying to join meeting", + "app.meeting.logout.permissionEjectReason": "Ejected due to permission violation", + "app.meeting.logout.ejectedFromMeeting": "You have been removed from the meeting", + "app.meeting.logout.validateTokenFailedEjectReason": "Failed to validate authorization token", + "app.meeting.logout.userInactivityEjectReason": "User inactive for too long", "app.dropdown.close": "Close", "app.error.500": "Ops, something went wrong", "app.error.404": "Not found", @@ -426,6 +431,7 @@ "app.lock-viewers.Layout": "Layout", "app.recording.startTitle": "Start recording", "app.recording.stopTitle": "Pause recording", + "app.recording.resumeTitle": "Resume recording", "app.recording.startDescription": "(You can select the record button again later to pause the recording.)", "app.recording.stopDescription": "Are you sure you want to pause the recording? (You can resume by selecting the record button again.)", "app.videoPreview.cameraLabel": "Camera", @@ -550,8 +556,10 @@ "app.externalVideo.stop": "Stop sharing video", "app.externalVideo.title": "Share a YouTube video", "app.externalVideo.input": "YouTube video URL", + "app.externalVideo.urlInput": "Add YouTube URL", "app.externalVideo.urlError": "This URL isn't a valid YouTube video", "app.externalVideo.close": "Close", - "app.actionsBar.actionsDropdown.shareExternalVideo": "Share Youtube video", - "app.actionsBar.actionsDropdown.stopShareExternalVideo": "Stop sharing video" + "app.externalVideo.noteLabel": "Note: Shared YouTube videos will not appear in the recording", + "app.actionsBar.actionsDropdown.shareExternalVideo": "Share YouTube video", + "app.actionsBar.actionsDropdown.stopShareExternalVideo": "Stop sharing YouTube video" } diff --git a/bigbluebutton-html5/private/locales/es.json b/bigbluebutton-html5/private/locales/es.json index c1148ba9d5a40fbc35b54f4cbdd8f570201f2903..fef8b665d4782fa9c329eb279aa7aa9db2119dde 100644 --- a/bigbluebutton-html5/private/locales/es.json +++ b/bigbluebutton-html5/private/locales/es.json @@ -378,7 +378,6 @@ "app.lock-viewers.PrivateChatLable": "Chat privado", "app.lock-viewers.Layout": "Diseño de pantalla", "app.recording.startTitle": "Iniciar grabación", - "app.recording.stopTitle": "Finalizar grabación", "app.recording.startDescription": "(Puedes hacer click en el icono nuevamente para detener la grabación)", "app.recording.stopDescription": "¿Quieres detener la grabación de la sesión? (Puedes continuar la grabación en cualquier momento haciendo click en el icono de iniciar grabación.)", "app.videoPreview.cameraLabel": "Webcam", diff --git a/bigbluebutton-html5/private/locales/es_MX.json b/bigbluebutton-html5/private/locales/es_MX.json index 887728be9b845a86e2adbc8c76298f136fe9ada3..51ed8ca076f7a522b7f6442eb3ea6b7b8a55465d 100644 --- a/bigbluebutton-html5/private/locales/es_MX.json +++ b/bigbluebutton-html5/private/locales/es_MX.json @@ -378,7 +378,6 @@ "app.lock-viewers.PrivateChatLable": "Chat privado", "app.lock-viewers.Layout": "Diseño de pantalla", "app.recording.startTitle": "Iniciar grabación", - "app.recording.stopTitle": "Finalizar grabación", "app.recording.startDescription": "(Puedes hacer click en el icono nuevamente para detener la grabación)", "app.recording.stopDescription": "¿Quieres detener la grabación de la sesión? (Puedes continuar la grabación en cualquier momento haciendo click en el icono de iniciar grabación.)", "app.videoPreview.cameraLabel": "Webcam", diff --git a/bigbluebutton-html5/private/locales/id.json b/bigbluebutton-html5/private/locales/id.json index 8ae5109770cc70463fddf6de06f737f7a3c10e55..836480ce1e9371eb854c21a28feaadfeef10df0e 100644 --- a/bigbluebutton-html5/private/locales/id.json +++ b/bigbluebutton-html5/private/locales/id.json @@ -29,6 +29,8 @@ "app.userList.menu.removeUser.label": "Hapus pengguna", "app.userList.menu.muteUserAudio.label": "Diamkan pengguna", "app.userList.menu.unmuteUserAudio.label": "Aktifkan pengguna", + "app.userList.menu.lockUser.label": "Kunci {0}", + "app.userList.userOptions.muteAllLabel": "Diamkan semua Pengguna", "app.media.label": "Media", "app.media.screenshare.start": "Berbagi layar sudah mulai", "app.media.screenshare.end": "Berbagi layar sudah berakhir", diff --git a/bigbluebutton-html5/private/locales/pt_BR.json b/bigbluebutton-html5/private/locales/pt_BR.json index efaf96b7605d1a59783529b549c48cf33eecba0e..e816cb9fc5e2182c01d363951c15837ef9aeaa4f 100644 --- a/bigbluebutton-html5/private/locales/pt_BR.json +++ b/bigbluebutton-html5/private/locales/pt_BR.json @@ -21,6 +21,10 @@ "app.note.title": "Notas compartilhadas", "app.note.label": "Nota", "app.note.hideNoteLabel": "Ocultar nota", + "app.user.activityCheck": "Verificação de atividade do usuário", + "app.user.activityCheck.label": "Verifica se o usuário ainda está na sala ({0})", + "app.user.activityCheck.check": "Verificar", + "app.note.tipLabel": "Pressione Esc para focar na barra de ferramentas do editor", "app.userList.usersTitle": "Usuários", "app.userList.participantsTitle": "Participantes", "app.userList.messagesTitle": "Mensagens", @@ -67,6 +71,10 @@ "app.meeting.alertMeetingEndsUnderOneMinute": "Sessão será fechada em um minuto.", "app.meeting.alertBreakoutEndsUnderOneMinute": "Sala de apoio será fechada em um minuto.", "app.presentation.close": "Fechar apresentação", + "app.presentation.slideContent": "Conteúdo do slide", + "app.presentation.startSlideContent": "InÃcio do conteúdo do slide", + "app.presentation.endSlideContent": "Fim do conteúdo do slide", + "app.presentation.emptySlideContent": "Nenhum conteúdo no slide atual", "app.presentation.presentationToolbar.prevSlideLabel": "Slide anterior", "app.presentation.presentationToolbar.prevSlideDesc": "Mudar a apresentação para o slide anterior", "app.presentation.presentationToolbar.nextSlideLabel": "Próximo slide", @@ -83,8 +91,10 @@ "app.presentation.presentationToolbar.zoomInDesc": "Aproximar a apresentação", "app.presentation.presentationToolbar.zoomOutLabel": "Afastar", "app.presentation.presentationToolbar.zoomOutDesc": "Afastar a apresentação", + "app.presentation.presentationToolbar.zoomReset": "Restaurar zoom", "app.presentation.presentationToolbar.zoomIndicator": "Percentual atual de zoom", "app.presentation.presentationToolbar.fitToWidth": "Ajustar à largura", + "app.presentation.presentationToolbar.fitToPage": "Ajustar à página", "app.presentation.presentationToolbar.goToSlide": "Slide {0}", "app.presentationUploder.title": "Apresentação", "app.presentationUploder.message": "Como apresentador, você tem a capacidade de carregar qualquer documento do Office ou arquivo PDF. Para melhores resultados, recomendamos o formato PDF.", @@ -109,7 +119,12 @@ "app.presentationUploder.conversion.generatingSvg": "Gerando imagens SVG...", "app.presentationUploder.conversion.pageCountExceeded": "Ops, a contagem de páginas excedeu o limite", "app.presentationUploder.conversion.timeout": "Ops, a conversão levou tempo demais", + "app.presentationUploder.isDownloadableLabel": "Não permitir download da apresentação", + "app.presentationUploder.isNotDownloadableLabel": "Permitir download da apresentação", + "app.presentationUploder.removePresentationLabel": "Remover apresentação", + "app.presentationUploder.setAsCurrentPresentation": "Usar apresentação", "app.poll.pollPaneTitle": "Enquete", + "app.poll.quickPollTitle": "Enquete rápida", "app.poll.hidePollDesc": "Ocultar menu de enquetes", "app.poll.customPollInstruction": "Para criar uma enquete personalizada, selecione o botão abaixo e digite as opções.", "app.poll.quickPollInstruction": "Selecione uma opção abaixo para iniciar uma enquete.", @@ -119,7 +134,12 @@ "app.poll.publishLabel": "Publicar resultados da enquete", "app.poll.backLabel": "Voltar para opções de enquete", "app.poll.closeLabel": "Fechar", + "app.poll.ariaInputCount": "Entrada {0} de {1}", "app.poll.customPlaceholder": "Adicionar opção na enquete", + "app.poll.noPresentationSelected": "Nenhuma apresentação selecionada! Por favor, selecione uma.", + "app.poll.clickHereToSelect": "Clique aqui para selecionar", + "app.poll.t": "Verdadeiro", + "app.poll.f": "Falso", "app.poll.tf": "Verdadeiro / Falso", "app.poll.y": "Sim", "app.poll.n": "Não", @@ -134,7 +154,9 @@ "app.polling.pollAnswerLabel": "Resposta da enquete {0}", "app.polling.pollAnswerDesc": "Selecione esta opção para votar em {0}", "app.failedMessage": "Desculpe-nos, estamos com problemas na conexão com o servidor", + "app.downloadPresentationButton.label": "Download da apresentação original", "app.connectingMessage": "Conectando ...", + "app.waitingMessage": "Desconectado. Reconectando em {0} segundos ...", "app.navBar.settingsDropdown.optionsLabel": "Opções", "app.navBar.settingsDropdown.fullscreenLabel": "Alternar para tela cheia", "app.navBar.settingsDropdown.settingsLabel": "Abrir configurações", @@ -153,6 +175,7 @@ "app.navBar.settingsDropdown.endMeetingDesc": "Encerra a sessão atual", "app.navBar.settingsDropdown.endMeetingLabel": "Encerrar sessão", "app.navBar.userListToggleBtnLabel": "Alternar lista de usuários", + "app.navBar.toggleUserList.ariaLabel": "Alternar usuários e mensagens", "app.navBar.toggleUserList.newMessages": "com notificação para novas mensagens", "app.navBar.recording": "Esta conferência está sendo gravada", "app.navBar.recording.on": "A gravação está em andamento", @@ -173,15 +196,21 @@ "app.actionsBar.changeStatusLabel": "Alterar status", "app.actionsBar.muteLabel": "Silenciar", "app.actionsBar.unmuteLabel": "Falar", + "app.actionsBar.camOffLabel": "Câmera desligada", "app.actionsBar.raiseLabel": "Levantar a mão", + "app.actionsBar.label": "Barra de ações", + "app.actionsBar.actionsDropdown.restorePresentationLabel": "Restaurar apresentação", "app.actionsBar.actionsDropdown.restorePresentationDesc": "Restaura a apresentação depois que ela foi fechada", + "app.screenshare.screenShareLabel" : "Compartilhamento de tela", "app.submenu.application.applicationSectionTitle": "Aplicação", + "app.submenu.application.animationsLabel": "Animações", "app.submenu.application.audioAlertLabel": "Alertas de áudio para bate-papo", "app.submenu.application.pushAlertLabel": "Alertas de pop-up para bate-papo", "app.submenu.application.fontSizeControlLabel": "Tamanho da fonte", "app.submenu.application.increaseFontBtnLabel": "Aumentar o tamanho da fonte da aplicação", "app.submenu.application.decreaseFontBtnLabel": "Diminuir o tamanho da fonte da aplicação", "app.submenu.application.languageLabel": "Idioma da aplicação", + "app.submenu.application.ariaLanguageLabel": "Alterar idioma da aplicação", "app.submenu.application.languageOptionLabel": "Escolha o idioma", "app.submenu.application.noLocaleOptionLabel": "Não existem esquemas de idiomas disponÃveis", "app.submenu.audio.micSourceLabel": "Seleção do microfone", @@ -190,18 +219,29 @@ "app.submenu.video.title": "VÃdeo", "app.submenu.video.videoSourceLabel": "Seleção do vÃdeo", "app.submenu.video.videoOptionLabel": "Selecione a entrada de vÃdeo", + "app.submenu.video.videoQualityLabel": "Qualidade do vÃdeo", "app.submenu.video.qualityOptionLabel": "Escolha a qualidade do vÃdeo", "app.submenu.video.participantsCamLabel": "Visualizando webcams dos participantes", + "app.submenu.closedCaptions.closedCaptionsLabel": "Legendas", "app.submenu.closedCaptions.takeOwnershipLabel": "Assumir o controle", "app.submenu.closedCaptions.languageLabel": "Idioma", "app.submenu.closedCaptions.localeOptionLabel": "Escolha o idioma", "app.submenu.closedCaptions.noLocaleOptionLabel": "Nenhum esquema de idioma ativo", "app.submenu.closedCaptions.fontFamilyLabel": "FamÃlia da fonte", + "app.submenu.closedCaptions.fontFamilyOptionLabel": "Selecionar famÃlia da fonte", "app.submenu.closedCaptions.fontSizeLabel": "Tamanho da fonte", + "app.submenu.closedCaptions.fontSizeOptionLabel": "Escolha o tamanho da fonte", "app.submenu.closedCaptions.backgroundColorLabel": "Cor de fundo", "app.submenu.closedCaptions.fontColorLabel": "Cor da fonte", "app.submenu.closedCaptions.noLocaleSelected": "Idioma não selecionado", "app.submenu.participants.muteAllLabel": "Silenciar todos exceto o apresentador", + "app.submenu.participants.lockAllLabel": "Restringir todos os participantes", + "app.submenu.participants.lockItemLabel": "Participantes {0}", + "app.submenu.participants.lockMicDesc": "Desativa o microfone para todos os participantes bloqueados", + "app.submenu.participants.lockCamDesc": "Desativa a webcam para todos os participantes bloqueados", + "app.submenu.participants.lockPublicChatDesc": "Desativa o bate-papo público para todos os participantes bloqueados", + "app.submenu.participants.lockPrivateChatDesc": "Desativa o bate-papo privado para todos os participantes bloqueados", + "app.submenu.participants.lockLayoutDesc": "Bloqueia o layout para todos os participantes bloqueados", "app.submenu.participants.lockMicAriaLabel": "Bloqueia o microfone", "app.submenu.participants.lockCamAriaLabel": "Bloqueia a webcam", "app.submenu.participants.lockPublicChatAriaLabel": "Bloqueia o bate-papo público", @@ -209,16 +249,22 @@ "app.submenu.participants.lockLayoutAriaLabel": "Bloqueia o layout", "app.submenu.participants.lockMicLabel": "Microfone", "app.submenu.participants.lockCamLabel": "Webcam", + "app.submenu.participants.lockPublicChatLabel": "Bate-papo público", + "app.submenu.participants.lockPrivateChatLabel": "Bate-papo privado", "app.submenu.participants.lockLayoutLabel": "Layout", "app.settings.applicationTab.label": "Aplicação", "app.settings.audioTab.label": "Ãudio", "app.settings.videoTab.label": "VÃdeo", + "app.settings.closedcaptionTab.label": "Legendas", "app.settings.usersTab.label": "Participantes", "app.settings.main.label": "Configurações", "app.settings.main.cancel.label": "Cancelar", "app.settings.main.cancel.label.description": "Descartar as alterações e fechar o menu de configurações", "app.settings.main.save.label": "Salvar", "app.settings.main.save.label.description": "Salvar as alterações e fechar o menu de configurações", + "app.settings.dataSavingTab.label": "Economia de dados", + "app.settings.dataSavingTab.webcam": "Ativar webcams", + "app.settings.dataSavingTab.screenShare": "Ativar o compartilhamento de tela", "app.settings.dataSavingTab.description": "Para economizar o volume de transferência de dados, ajuste o que está sendo exibido no momento.", "app.switch.onLabel": "Ligar", "app.switch.offLabel": "Desligar", @@ -237,6 +283,7 @@ "app.actionsBar.actionsDropdown.createBreakoutRoomDesc": "Cria salas de apoio para dividir a sessão atual em grupos", "app.actionsBar.actionsDropdown.takePresenter": "Assumir papel de apresentador", "app.actionsBar.actionsDropdown.takePresenterDesc": "Assume o papel de apresentador", + "app.actionsBar.emojiMenu.statusTriggerLabel": "Definir status", "app.actionsBar.emojiMenu.awayLabel": "Ausente", "app.actionsBar.emojiMenu.awayDesc": "Mudar seu status para ausente", "app.actionsBar.emojiMenu.raiseHandLabel": "Levantar a mão", @@ -261,6 +308,7 @@ "app.audioNotification.audioFailedError1001": "Erro 1001: WebSocket desconectou", "app.audioNotification.audioFailedError1002": "Erro 1002: Não foi possÃvel estabelecer a conexão WebSocket", "app.audioNotification.audioFailedError1003": "Erro 1003: Versão do navegador não suportada", + "app.audioNotification.audioFailedError1004": "Erro 1004: Falha na chamada (causa={0})", "app.audioNotification.audioFailedError1005": "Erro 1005: Chamada encerrada inesperadamente", "app.audioNotification.audioFailedError1006": "Erro 1006: Tempo de espera da chamada expirou", "app.audioNotification.audioFailedError1007": "Erro 1007: Negociação ICE falhou", @@ -268,14 +316,25 @@ "app.audioNotification.audioFailedError1009": "Erro 1009: Não foi possÃvel recuperar as informações do servidor STUN/TURN", "app.audioNotification.audioFailedError1010": "Erro 1010: Negociação ICE expirou", "app.audioNotification.audioFailedError1011": "Erro 1011: Coleta de candidatos ICE expirou", + "app.audioNotification.audioFailedError1012": "Erro 1012: Conexão ICE fechou", "app.audioNotification.audioFailedMessage": "Conexão de áudio não pôde ser estabelecida", + "app.audioNotification.mediaFailedMessage": "getUserMicMedia falhou, somente origens seguras são permitidas", "app.audioNotification.closeLabel": "Fechar", + "app.audioNotificaion.reconnectingAsListenOnly": "O microfone foi bloqueado para participantes, você está sendo conectado como ouvinte", + "app.breakoutJoinConfirmation.title": "Entrar na sala de apoio", "app.breakoutJoinConfirmation.message": "Gostaria de participar", "app.breakoutJoinConfirmation.confirmLabel": "Entrar", + "app.breakoutJoinConfirmation.confirmDesc": "Entrar na sala de apoio", "app.breakoutJoinConfirmation.dismissLabel": "Cancelar", + "app.breakoutJoinConfirmation.dismissDesc": "Fechar e rejeitar o convite a sala de apoio", + "app.breakoutJoinConfirmation.freeJoinMessage": "Escolha uma sala de apoio para entrar", + "app.breakoutTimeRemainingMessage": "Tempo restante na sala de apoio: {0}", + "app.breakoutWillCloseMessage": "Tempo expirado. A sala de apoio será fechada em breve", + "app.calculatingBreakoutTimeRemaining": "Calculando o tempo restante...", "app.audioModal.microphoneLabel": "Microfone", "app.audioModal.listenOnlyLabel": "Somente ouvir", "app.audioModal.audioChoiceLabel": "Como você gostaria de se juntar ao áudio?", + "app.audioModal.iOSBrowser": "Ãudio/VÃdeo não é suportado", "app.audioModal.iOSErrorDescription": "No momento, o Chrome não suporta áudio e vÃdeo para iOS.", "app.audioModal.iOSErrorRecommendation": "Recomendamos o Safari para usar o iOS.", "app.audioModal.audioChoiceDesc": "Selecione como se juntar ao áudio nesta reunião", @@ -287,6 +346,7 @@ "app.audioModal.echoTestTitle": "Este é um teste de som privado. Fale algumas palavras. Você consegue escutar o som?", "app.audioModal.settingsTitle": "Alterar as configurações de áudio", "app.audioModal.helpTitle": "Houve um problema com seus dispositivos de mÃdia", + "app.audioModal.helpText": "Você autorizou o acesso ao seu microfone? Observe que uma caixa de diálogo deve abrir assim que você tentar habilitar o microfone solicitando permissão de acesso aos dispositivos de mÃdia. Por favor autorize para que a aplicação tenha acesso ao seu microfone. Se isso não funcionar, tente alterar a permissão do microfone nas configurações do seu navegador.", "app.audioModal.connecting": "Conectando", "app.audioModal.connectingEchoTest": "Conectando ao teste de áudio", "app.audioManager.joinedAudio": "Você se juntou à conferência de áudio", @@ -299,6 +359,8 @@ "app.audioManager.mediaError": "Erro: Houve um problema ao consultar seus dispositivos de mÃdia", "app.audio.joinAudio": "Ativar áudio", "app.audio.leaveAudio": "Desativar áudio", + "app.audio.enterSessionLabel": "Entrar na sessão", + "app.audio.playSoundLabel": "Tocar som de teste", "app.audio.backLabel": "Voltar", "app.audio.audioSettings.titleLabel": "Configurações de áudio", "app.audio.audioSettings.descriptionLabel": "Uma caixa de diálogo aparecerá em seu navegador, onde você deve concordar em compartilhar o seu microfone.", @@ -309,6 +371,7 @@ "app.audio.listenOnly.backLabel": "Voltar", "app.audio.listenOnly.closeLabel": "Fechar", "app.audio.permissionsOverlay.title": "Permitir que o BigBlueButton acesse seus dispositivos de mÃdia", + "app.audio.permissionsOverlay.hint": "Precisamos que você nos permita acessar seus dispositivos de mÃdia habilitar seu microfone", "app.error.removed": "Você foi removido da conferência", "app.error.meeting.ended": "Você saiu da conferência", "app.dropdown.close": "Fechar", @@ -319,12 +382,25 @@ "app.error.400": "400 Solicitação incorreta", "app.error.leaveLabel": "Faça o login novamente", "app.guest.waiting": "Esperando aprovação para participar", + "app.userList.guest.waitingUsers": "Usuários aguardando", + "app.userList.guest.waitingUsersTitle": "Gerenciamento de usuários", + "app.userList.guest.optionTitle": "Usuários aguardando revisão", + "app.userList.guest.allowAllAuthenticated": "Permitir todos os usuários autorizados", + "app.userList.guest.allowAllGuests": "Permitir todos os convidados", + "app.userList.guest.allowEveryone": "Permitir todos", + "app.userList.guest.denyEveryone": "Rejeitar todos", + "app.userList.guest.pendingUsers": "{0} usuários aguardando", + "app.userList.guest.pendingGuestUsers": "{0} usuários convidados aguardando", + "app.userList.guest.pendingGuestAlert": "entrou na sessão e está aguardando sua permissão.", + "app.toast.breakoutRoomEnded": "A sala de apoio encerrou. Por favor, acione o áudio novamente.", "app.toast.chat.public": "Nova mensagem de Bate-papo público", "app.toast.chat.private": "Nova mensagem de Bate-papo privado", "app.toast.chat.system": "Sistema", "app.notification.recordingStart": "Esta reunião está sendo gravada", "app.notification.recordingStop": "Esta reunião não está mais sendo gravada", + "app.notification.recordingAriaLabel": "Tempo de gravação", "app.shortcut-help.title": "Atalhos de teclado", + "app.shortcut-help.accessKeyNotAvailable": "Chaves de acesso não estão disponÃveis", "app.shortcut-help.comboLabel": "Combo", "app.shortcut-help.functionLabel": "Função", "app.shortcut-help.closeLabel": "Fechar", @@ -332,14 +408,25 @@ "app.shortcut-help.openOptions": "Abrir Configurações", "app.shortcut-help.toggleUserList": "Abrir / fechar a lista de usuários", "app.shortcut-help.toggleMute": "Mudo / Falar", + "app.shortcut-help.togglePublicChat": "Alternar bate-papo público (a lista de usuários deve estar aberta)", + "app.shortcut-help.hidePrivateChat": "Esconder bate-papo privado", + "app.shortcut-help.closePrivateChat": "Fechar bate-papo privado", + "app.shortcut-help.openActions": "Abrir menu de ações", + "app.shortcut-help.openStatus": "Abrir menu de status", "app.lock-viewers.title": "Restringir participantes", + "app.lock-viewers.description": "Estas opções permitem que você restrinja aos participantes o acesso a funcionalidades especÃficas (as restrições não se aplicam aos moderadores)", "app.lock-viewers.featuresLable": "Funcionalidade", + "app.lock-viewers.lockStatusLabel": "Status de restrição", "app.lock-viewers.webcamLabel": "Webcam", "app.lock-viewers.otherViewersWebcamLabel": "Ver webcam de outros participantes", "app.lock-viewers.microphoneLable": "Microfone", + "app.lock-viewers.PublicChatLabel": "Bate-papo público", + "app.lock-viewers.PrivateChatLable": "Bate-papo privado", "app.lock-viewers.Layout": "Layout", "app.recording.startTitle": "Iniciar gravação", - "app.recording.stopTitle": "Parar gravação", + "app.recording.stopTitle": "Pausar gravação", + "app.recording.startDescription": "(Você pode clicar novamente no botão de gravação para pausar a gravação)", + "app.recording.stopDescription": "Você tem certeza que deseja pausar a gravação? (Você pode retomar a gravação a qualquer momento clicando novamente no botão de gravação)", "app.videoPreview.cameraLabel": "Câmera", "app.videoPreview.cancelLabel": "Cancelar", "app.videoPreview.closeLabel": "Fechar", @@ -350,6 +437,7 @@ "app.videoPreview.webcamNotFoundLabel": "Webcam não encontrada", "app.video.joinVideo": "Compartilhar webcam", "app.video.leaveVideo": "Parar de compartilhar webcam", + "app.video.iceCandidateError": "Erro ao adicionar o candidato ICE", "app.video.iceConnectionStateError": "Erro 1107: Negociação ICE falhou", "app.video.permissionError": "Erro ao compartilhar webcam. Por favor verifique as permissões", "app.video.sharingError": "Erro ao compartilhar a webcam", @@ -363,8 +451,10 @@ "app.video.videoDisabled": "Webcam está desabilitada", "app.video.videoButtonDesc": "Botão de habilitar vÃdeo", "app.video.videoMenu": "Menu de vÃdeo", + "app.video.videoMenuDisabled": "Webcam desativada nas configurações", "app.video.videoMenuDesc": "Abra o menu de vÃdeo", "app.video.chromeExtensionError": "Você deve instalar o seguinte:", + "app.video.chromeExtensionErrorLink": "esta extensão do Chrome", "app.video.stats.title": "EstatÃsticas de conexão", "app.video.stats.packetsReceived": "Pacotes recebidos", "app.video.stats.packetsSent": "Pacotes enviados", @@ -378,6 +468,7 @@ "app.video.stats.rtt": "RTT", "app.video.stats.encodeUsagePercent": "Uso de codificação", "app.video.stats.currentDelay": "Atraso atual", + "app.fullscreenButton.label": "Alternar {0} para tela cheia", "app.deskshare.iceConnectionStateError": "Erro 1108: Falha na conexão ICE ao compartilhar a tela", "app.sfu.mediaServerConnectionError2000": "Erro 2000: Não foi possÃvel conectar ao servidor de mÃdia", "app.sfu.mediaServerOffline2001": "Erro 2001: Servidor de mÃdia está offline. Por favor, tente novamente mais tarde.", @@ -397,13 +488,18 @@ "app.whiteboard.toolbar.tools.ellipse": "Elipse", "app.whiteboard.toolbar.tools.line": "Linha", "app.whiteboard.toolbar.tools.text": "Texto", + "app.whiteboard.toolbar.thickness": "Espessura da linha", + "app.whiteboard.toolbar.thicknessDisabled": "A espessura da linha está desativada", "app.whiteboard.toolbar.color": "Cores", + "app.whiteboard.toolbar.colorDisabled": "As cores estão desativadas", "app.whiteboard.toolbar.color.black": "Preto", "app.whiteboard.toolbar.color.white": "Branco", "app.whiteboard.toolbar.color.red": "Vermelho", "app.whiteboard.toolbar.color.orange": "Laranja", + "app.whiteboard.toolbar.color.eletricLime": "Verde limão", "app.whiteboard.toolbar.color.lime": "Verde", "app.whiteboard.toolbar.color.cyan": "Ciano", + "app.whiteboard.toolbar.color.dodgerBlue": "Azul dodger", "app.whiteboard.toolbar.color.blue": "Azul", "app.whiteboard.toolbar.color.violet": "Violeta", "app.whiteboard.toolbar.color.magenta": "Magenta", @@ -412,10 +508,12 @@ "app.whiteboard.toolbar.clear": "Limpar todas as anotações", "app.whiteboard.toolbar.multiUserOn": "Iniciar o modo multiusuário", "app.whiteboard.toolbar.multiUserOff": "Sair do modo multiusuário", + "app.whiteboard.toolbar.fontSize": "Lista de tamanhos de fonte", "app.feedback.title": "Você saiu da conferência", "app.feedback.subtitle": "AdorarÃamos ouvir sobre sua experiência com o BigBlueButton (opcional)", "app.feedback.textarea": "Como podemos melhorar o BigBlueButton?", "app.feedback.sendFeedback": "Enviar feedback", + "app.feedback.sendFeedbackDesc": "Enviar um feedback e deixar a sessão", "app.videoDock.webcamFocusLabel": "Focar", "app.videoDock.webcamFocusDesc": "Focar a webcam selecionada", "app.videoDock.webcamUnfocusLabel": "Desfocar", @@ -423,24 +521,37 @@ "app.invitation.title": "Convite para sala de apoio", "app.invitation.confirm": "Convidar", "app.createBreakoutRoom.title": "Salas de apoio", + "app.createBreakoutRoom.ariaTitle": "Esconder salas de apoio", "app.createBreakoutRoom.breakoutRoomLabel": "Salas de apoio {0}", "app.createBreakoutRoom.generatingURL": "Gerando URL", "app.createBreakoutRoom.generatedURL": "Gerada", "app.createBreakoutRoom.duration": "Duração {0}", "app.createBreakoutRoom.room": "Sala {0}", + "app.createBreakoutRoom.notAssigned": "Não atribuÃdo ({0})", + "app.createBreakoutRoom.join": "Entrar na sala", "app.createBreakoutRoom.joinAudio": "Ativar áudio", "app.createBreakoutRoom.returnAudio": "Retornar áudio", "app.createBreakoutRoom.confirm": "Criar", + "app.createBreakoutRoom.numberOfRooms": "Número de salas", "app.createBreakoutRoom.durationInMinutes": "Duração (minutos)", + "app.createBreakoutRoom.randomlyAssign": "Atribuir aleatoriamente", + "app.createBreakoutRoom.endAllBreakouts": "Encerrar todas as salas de apoio", "app.createBreakoutRoom.roomName": "{0} (Sala - {1})", "app.createBreakoutRoom.doneLabel": "Pronto", "app.createBreakoutRoom.nextLabel": "Próximo", + "app.createBreakoutRoom.minusRoomTime": "Aumentar tempo de sala de apoio", "app.createBreakoutRoom.addParticipantLabel": "+ adicionar participante", "app.createBreakoutRoom.freeJoin": "Permitir que os usuários escolham uma sala de apoio para entrar", "app.createBreakoutRoom.leastOneWarnBreakout": "Você precisa atribuir pelo menos um usuário a uma sala de apoio", "app.createBreakoutRoom.modalDesc": "Complete os passos abaixo para criar salas de apoio na sessão", "app.externalVideo.start": "Compartilhar vÃdeo", - "app.externalVideo.close": "Fechar" + "app.externalVideo.stop": "Interromper compartilhamento do vÃdeo", + "app.externalVideo.title": "Compartilhar um vÃdeo do YouTube", + "app.externalVideo.input": "URL do vÃdeo no YouTube", + "app.externalVideo.urlError": "Esta não é uma URL válida para um vÃdeo no YouTube", + "app.externalVideo.close": "Fechar", + "app.actionsBar.actionsDropdown.shareExternalVideo": "Compartilhar vÃdeo do YouTube", + "app.actionsBar.actionsDropdown.stopShareExternalVideo": "Interromper compartilhamento do vÃdeo" } diff --git a/bigbluebutton-html5/private/locales/uk_UA.json b/bigbluebutton-html5/private/locales/uk_UA.json index 10d7b86b10d821763edf625a14b443124569320f..679ed82a26a2d2b35b1ce507f377e70212bd48ee 100644 --- a/bigbluebutton-html5/private/locales/uk_UA.json +++ b/bigbluebutton-html5/private/locales/uk_UA.json @@ -404,7 +404,6 @@ "app.lock-viewers.PrivateChatLable": "Приватний чат", "app.lock-viewers.Layout": "Ð Ð¾Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÐ¾Ð½", "app.recording.startTitle": "Почати запиÑ", - "app.recording.stopTitle": "Зупинити запиÑ", "app.recording.startDescription": "(Ви зможете натиÑнути цю кнопку пізніше, щоб зробити паузу запиÑу.)", "app.recording.stopDescription": "Ви впевнені що хочете зробити паузу запиÑу? (Ви зможете відновити запиÑ, натиÑнувши кнопку запиÑу знову.)", "app.videoPreview.cameraLabel": "Камера", @@ -520,7 +519,6 @@ "app.createBreakoutRoom.doneLabel": "Готово", "app.createBreakoutRoom.nextLabel": "Далі", "app.createBreakoutRoom.minusRoomTime": "Збільшити Ñ‡Ð°Ñ ÐºÑ–Ð¼Ð½Ð°Ñ‚Ð¸", - "app.createBreakoutRoom.addRoomTime": "Зменшити Ñ‡Ð°Ñ ÐºÑ–Ð¼Ð½Ð°Ñ‚Ð¸", "app.createBreakoutRoom.addParticipantLabel": "+ Додати учаÑника", "app.createBreakoutRoom.freeJoin": "Дозволити кориÑтувачам обирати кімнату ÑамоÑтійно", "app.createBreakoutRoom.leastOneWarnBreakout": "Щонайменш один кориÑтувач повинен бути в кімнаті.", diff --git a/bigbluebutton-html5/private/locales/zh_CN.json b/bigbluebutton-html5/private/locales/zh_CN.json index b1df5952508d6f49fa5ae640439dd7903ad024cd..9f819a1fc9cf8a3e9900b125545aa7af0b8fc2ae 100644 --- a/bigbluebutton-html5/private/locales/zh_CN.json +++ b/bigbluebutton-html5/private/locales/zh_CN.json @@ -404,7 +404,6 @@ "app.lock-viewers.PrivateChatLable": "ç§èŠ", "app.lock-viewers.Layout": "页é¢å¸ƒå±€", "app.recording.startTitle": "开始录制", - "app.recording.stopTitle": "结æŸå½•åˆ¶", "app.recording.startDescription": "(您ç¨åŽå¯ä»¥å†ä¸€æ¬¡ç‚¹å‡»å½•åˆ¶æŒ‰é’®ä»¥æš‚åœå½•åˆ¶ã€‚)", "app.recording.stopDescription": "您确定è¦æš‚åœå½•åˆ¶å—?(您å¯ä»¥å†ä¸€æ¬¡ç‚¹å‡»å½•åˆ¶æŒ‰é’®ä»¥é‡æ–°å½•åˆ¶ã€‚)", "app.videoPreview.cameraLabel": "æ‘„åƒå¤´", @@ -520,7 +519,6 @@ "app.createBreakoutRoom.doneLabel": "完æˆ", "app.createBreakoutRoom.nextLabel": "下一æ¥", "app.createBreakoutRoom.minusRoomTime": "延长分组讨论时间", - "app.createBreakoutRoom.addRoomTime": "缩çŸåˆ†ç»„讨论时间", "app.createBreakoutRoom.addParticipantLabel": "+ æ·»åŠ å‚会人", "app.createBreakoutRoom.freeJoin": "å…è®¸äººå‘˜é€‰æ‹©å¹¶åŠ å…¥åˆ†ç»„è®¨è®º", "app.createBreakoutRoom.leastOneWarnBreakout": "您必须至少指派一ä½äººå‘˜åˆ°æ¯ä¸€ä¸ªåˆ†ç»„讨论", diff --git a/bigbluebutton-web/.gitignore b/bigbluebutton-web/.gitignore index 753212053b60654efaecbc7d3668aef5edf182c1..3b68951979812d13e426a2f88ae69034ac39a1ed 100755 --- a/bigbluebutton-web/.gitignore +++ b/bigbluebutton-web/.gitignore @@ -7,3 +7,6 @@ target/* bin/* plugins/* /bin/ +build/ +logs/ + diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties index eb3dbcc3c44a6588c9f7666d0e9876b78f7946c1..c69ac37b87ed2e46f7b94c965a5264f83bc1b50d 100755 --- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties +++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties @@ -84,6 +84,11 @@ defineTextThreshold=2000 #------------------------------------ numConversionThreads=2 +#---------------------------------------------------- +# Conversion of the presentation slides to SWF to be +# used in the Flash client +swfSlidesRequired=true + #---------------------------------------------------- # Additional conversion of the presentation slides to SVG # to be used in the HTML5 client diff --git a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml index 7f8f2a6d6934a128199e119b96e87b75036a0af8..3de17fe5a92d06685a7b27783c9214349c59d7e8 100755 --- a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml +++ b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml @@ -99,6 +99,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <property name="maxSwfFileSize" value="${MAX_SWF_FILE_SIZE}"/> <property name="maxConversionTime" value="${maxConversionTime}"/> <property name="swfSlidesGenerationProgressNotifier" ref="swfSlidesGenerationProgressNotifier"/> + <property name="swfSlidesRequired" value="${swfSlidesRequired}"/> <property name="svgImagesRequired" value="${svgImagesRequired}"/> <property name="generatePngs" value="${generatePngs}"/> </bean>