diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/meeting/ValidateConnAuthTokenSysMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/meeting/ValidateConnAuthTokenSysMsgHdlr.scala new file mode 100755 index 0000000000000000000000000000000000000000..1b0ac7a6a1c202273f84222037037c96baef20df --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/meeting/ValidateConnAuthTokenSysMsgHdlr.scala @@ -0,0 +1,30 @@ +package org.bigbluebutton.core.apps.meeting + +import org.bigbluebutton.common2.msgs.ValidateConnAuthTokenSysMsg +import org.bigbluebutton.core.models.RegisteredUsers +import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } +import org.bigbluebutton.core2.message.senders.MsgBuilder + +trait ValidateConnAuthTokenSysMsgHdlr { + val liveMeeting: LiveMeeting + val outGW: OutMsgRouter + + def handleValidateConnAuthTokenSysMsg(msg: ValidateConnAuthTokenSysMsg): Unit = { + val regUser = RegisteredUsers.getRegisteredUserWithToken( + msg.body.authToken, + msg.body.userId, + liveMeeting.registeredUsers + ) + + regUser match { + case Some(u) => + val event = MsgBuilder.buildValidateConnAuthTokenSysRespMsg(msg.body.meetingId, msg.body.userId, + msg.body.authToken, true, msg.body.conn) + outGW.send(event) + case None => + val event = MsgBuilder.buildValidateConnAuthTokenSysRespMsg(msg.body.meetingId, msg.body.userId, + msg.body.authToken, false, msg.body.conn) + outGW.send(event) + } + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/CreateNewPresentationPodPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/CreateNewPresentationPodPubMsgHdlr.scala old mode 100644 new mode 100755 index 54af514c2e0214acf8d60966e74289ded740ffab..1b3b3f73ef8c61cb99ef9c901292edaa8ddfa718 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/CreateNewPresentationPodPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/CreateNewPresentationPodPubMsgHdlr.scala @@ -1,53 +1,59 @@ package org.bigbluebutton.core.apps.presentationpod +import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.models.PresentationPod import org.bigbluebutton.core.running.LiveMeeting -trait CreateNewPresentationPodPubMsgHdlr { +trait CreateNewPresentationPodPubMsgHdlr extends SystemConfiguration { this: PresentationPodHdlrs => def handle(msg: CreateNewPresentationPodPubMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = { - def buildCreateNewPresentationPodEvtMsg(meetingId: String, ownerId: String, podId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, ownerId) - val envelope = BbbCoreEnvelope(CreateNewPresentationPodEvtMsg.NAME, routing) - val header = BbbClientMsgHeader(CreateNewPresentationPodEvtMsg.NAME, meetingId, ownerId) - - val body = CreateNewPresentationPodEvtMsgBody(ownerId, podId) - val event = CreateNewPresentationPodEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to eject user from meeting." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def buildCreateNewPresentationPodEvtMsg(meetingId: String, ownerId: String, podId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, ownerId) + val envelope = BbbCoreEnvelope(CreateNewPresentationPodEvtMsg.NAME, routing) + val header = BbbClientMsgHeader(CreateNewPresentationPodEvtMsg.NAME, meetingId, ownerId) + + val body = CreateNewPresentationPodEvtMsgBody(ownerId, podId) + val event = CreateNewPresentationPodEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } - val ownerId = msg.body.ownerId + val ownerId = msg.body.ownerId - val resultPod: PresentationPod = PresentationPodsApp.getPresentationPod(state, "DEFAULT_PRESENTATION_POD") match { - case None => PresentationPodsApp.createDefaultPresentationPod(ownerId) - case Some(pod) => { - if (pod.ownerId == "") { - PresentationPodsApp.changeOwnershipOfDefaultPod(state, ownerId).get - } else { - PresentationPodsApp.createPresentationPod(ownerId) + val resultPod: PresentationPod = PresentationPodsApp.getPresentationPod(state, "DEFAULT_PRESENTATION_POD") match { + case None => PresentationPodsApp.createDefaultPresentationPod(ownerId) + case Some(pod) => { + if (pod.ownerId == "") { + PresentationPodsApp.changeOwnershipOfDefaultPod(state, ownerId).get + } else { + PresentationPodsApp.createPresentationPod(ownerId) + } } } - } - - val respMsg = buildCreateNewPresentationPodEvtMsg( - liveMeeting.props.meetingProp.intId, - ownerId, resultPod.id - ) - bus.outGW.send(respMsg) - val pods = state.presentationPodManager.addPod(resultPod) + val respMsg = buildCreateNewPresentationPodEvtMsg( + liveMeeting.props.meetingProp.intId, + ownerId, resultPod.id + ) + bus.outGW.send(respMsg) - log.warning("_____ pres pod add, after:" + pods.getNumberOfPods()) - log.warning("_____ CreateNewPresentationPodPubMsgHdlr _" + pods.printPods()) + val pods = state.presentationPodManager.addPod(resultPod) - state.update(pods) + state.update(pods) + } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetAllPresentationPodsReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetAllPresentationPodsReqMsgHdlr.scala old mode 100644 new mode 100755 index 442598849901bc7163b3aa17711af75f50c416c2..ab3eddeb079c0b39aca1c52a4ee32b6652bef70d --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetAllPresentationPodsReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetAllPresentationPodsReqMsgHdlr.scala @@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.domain.{ PresentationPodVO, PresentationVO } import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.models.PresentationPod @@ -13,26 +14,34 @@ trait GetAllPresentationPodsReqMsgHdlr { def handle(msg: GetAllPresentationPodsReqMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = { - def buildGetAllPresentationPodsRespMsg(pods: Vector[PresentationPodVO], requesterId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, requesterId) - val envelope = BbbCoreEnvelope(GetAllPresentationPodsRespMsg.NAME, routing) - val header = BbbClientMsgHeader(GetAllPresentationPodsRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId) + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.GUEST_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to get all presentation pods from meeting." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def buildGetAllPresentationPodsRespMsg(pods: Vector[PresentationPodVO], requesterId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, requesterId) + val envelope = BbbCoreEnvelope(GetAllPresentationPodsRespMsg.NAME, routing) + val header = BbbClientMsgHeader(GetAllPresentationPodsRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId) - val body = GetAllPresentationPodsRespMsgBody(pods) - val event = GetAllPresentationPodsRespMsg(header, body) + val body = GetAllPresentationPodsRespMsgBody(pods) + val event = GetAllPresentationPodsRespMsg(header, body) - BbbCommonEnvCoreMsg(envelope, event) - } + BbbCommonEnvCoreMsg(envelope, event) + } + + val requesterId = msg.body.requesterId - val requesterId = msg.body.requesterId + val pods = PresentationPodsApp.getAllPresentationPodsInMeeting(state) - val pods = PresentationPodsApp.getAllPresentationPodsInMeeting(state) + val podsVO = pods.map(pod => PresentationPodsApp.translatePresentationPodToVO(pod)) + val event = buildGetAllPresentationPodsRespMsg(podsVO, requesterId) - val podsVO = pods.map(pod => PresentationPodsApp.translatePresentationPodToVO(pod)) - val event = buildGetAllPresentationPodsRespMsg(podsVO, requesterId) + bus.outGW.send(event) - bus.outGW.send(event) + state + } - state } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetPresentationInfoReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetPresentationInfoReqMsgHdlr.scala old mode 100644 new mode 100755 index 98da14b504af1dbcf7e9a6f3b8028b23642540a3..bcecbfa096c00a37992c26cfaf1d451331429c30 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetPresentationInfoReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/GetPresentationInfoReqMsgHdlr.scala @@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.domain.PresentationVO import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.LiveMeeting @@ -12,34 +13,42 @@ trait GetPresentationInfoReqMsgHdlr { def handle(msg: GetPresentationInfoReqMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = { - def buildGetPresentationInfoRespMsg(presentations: Vector[PresentationVO], podId: String, - requesterId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, requesterId) - val envelope = BbbCoreEnvelope(GetPresentationInfoRespMsg.NAME, routing) - val header = BbbClientMsgHeader(GetPresentationInfoRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId) - - val body = GetPresentationInfoRespMsgBody(podId, presentations) - val event = GetPresentationInfoRespMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.GUEST_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission get presentation info from meeting." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def buildGetPresentationInfoRespMsg(presentations: Vector[PresentationVO], podId: String, + requesterId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, requesterId) + val envelope = BbbCoreEnvelope(GetPresentationInfoRespMsg.NAME, routing) + val header = BbbClientMsgHeader(GetPresentationInfoRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId) + + val body = GetPresentationInfoRespMsgBody(podId, presentations) + val event = GetPresentationInfoRespMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val requesterId = msg.body.userId + val podId = msg.body.podId + + for { + pod <- PresentationPodsApp.getPresentationPod(state, podId) + } yield { + val presInPod = pod.presentations + + val presVOs = presInPod.values.map { p => + PresentationVO(p.id, p.name, p.current, p.pages.values.toVector, p.downloadable) + }.toVector + val event = buildGetPresentationInfoRespMsg(presVOs, podId, requesterId) + + bus.outGW.send(event) + + } + state } - val requesterId = msg.body.userId - val podId = msg.body.podId - - for { - pod <- PresentationPodsApp.getPresentationPod(state, podId) - } yield { - val presInPod = pod.presentations - - val presVOs = presInPod.values.map { p => - PresentationVO(p.id, p.name, p.current, p.pages.values.toVector, p.downloadable) - }.toVector - val event = buildGetPresentationInfoRespMsg(presVOs, podId, requesterId) - - bus.outGW.send(event) - - } - state } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationConversionCompletedSysPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationConversionCompletedSysPubMsgHdlr.scala old mode 100644 new mode 100755 index 972009647f640cf03afdd5237b8e15a3730eef2a..2ac6696e2446602b49175dedad236d1deabb1816 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationConversionCompletedSysPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationConversionCompletedSysPubMsgHdlr.scala @@ -53,8 +53,6 @@ trait PresentationConversionCompletedSysPubMsgHdlr { pods = pods.addPresentationToPod(pod.id, pres) pods = pods.setCurrentPresentation(pod.id, pres.id) - log.warning("_____PresentationConversionCompletedSysPubMsgHdlr_ " + pods.printPods()) - state.update(pods) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationPodsApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationPodsApp.scala old mode 100644 new mode 100755 index 622c8470cdebb8c20912f2ce9b23e63b2a3fc37d..309de533df5c8b4c2f0b6d25cd97d97c0a26950d --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationPodsApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/PresentationPodsApp.scala @@ -72,7 +72,6 @@ object PresentationPodsApp { for { defPod <- getPresentationPod(state, "DEFAULT_PRESENTATION_POD") } yield { - println(s"\n\n\n changeOwnershipOfDefaultPod $newOwnerId \n\n\n") defPod.copy(ownerId = newOwnerId) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPodPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPodPubMsgHdlr.scala old mode 100644 new mode 100755 index 17997714475796e0c58971d29cc4c12c5e8d974e..32b898301b2de9eac99ecfed0a724ffad0b018bb --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPodPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPodPubMsgHdlr.scala @@ -1,6 +1,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.LiveMeeting @@ -11,59 +12,61 @@ trait RemovePresentationPodPubMsgHdlr { def handle(msg: RemovePresentationPodPubMsg, state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = { - def buildRemovePresentationPodEvtMsg(meetingId: String, ownerId: String, podId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, ownerId) - val envelope = BbbCoreEnvelope(RemovePresentationPodEvtMsg.NAME, routing) - val header = BbbClientMsgHeader(RemovePresentationPodEvtMsg.NAME, meetingId, ownerId) - - val body = RemovePresentationPodEvtMsgBody(ownerId, podId) - val event = RemovePresentationPodEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.MOD_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to remove presentation pod from meeting." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def buildRemovePresentationPodEvtMsg(meetingId: String, ownerId: String, podId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, ownerId) + val envelope = BbbCoreEnvelope(RemovePresentationPodEvtMsg.NAME, routing) + val header = BbbClientMsgHeader(RemovePresentationPodEvtMsg.NAME, meetingId, ownerId) + + val body = RemovePresentationPodEvtMsgBody(ownerId, podId) + val event = RemovePresentationPodEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val requesterId = msg.body.requesterId // TODO -- use it + + val newState = for { + pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId) + } yield { + + val ownerId = pod.ownerId + + val event = buildRemovePresentationPodEvtMsg( + liveMeeting.props.meetingProp.intId, + ownerId, pod.id + ) + + bus.outGW.send(event) + + val pods = state.presentationPodManager.removePod(pod.id) + state.update(pods) + } + + newState match { + case Some(ns) => ns + case None => state + } + + // TODO check if requesterId == ownerId + // TODO check about notifying only the list of authorized? + + // val respMsg = buildRemovePresentationPodEvtMsg( + // liveMeeting.props.meetingProp.intId, + // ownerId, pod.id + // ) + // bus.outGW.send(respMsg) + // + // log.warning("RemovePresentationPodPubMsgHdlr new podId=" + pod.id) + // + // val pods = state.presentationPodManager.removePod(pod) + // state.update(pods) } - val requesterId = msg.body.requesterId // TODO -- use it - - log.warning(s"_____ attempt for pres pod removal by $requesterId, num before:${state.presentationPodManager.getNumberOfPods()}") - - val newState = for { - pod <- PresentationPodsApp.getPresentationPod(state, msg.body.podId) - } yield { - - val ownerId = pod.ownerId - - val event = buildRemovePresentationPodEvtMsg( - liveMeeting.props.meetingProp.intId, - ownerId, pod.id - ) - - bus.outGW.send(event) - - log.warning("_____ pres pod removal, before:" + state.presentationPodManager.getNumberOfPods()) - val pods = state.presentationPodManager.removePod(pod.id) - // PresentationPodsApp.removePresentationPod(state, pod.id) - log.warning("_____ pres pod removal, afterB:" + pods.getNumberOfPods()) - state.update(pods) - } - - newState match { - case Some(ns) => ns - case None => state - } - - // TODO check if requesterId == ownerId - // TODO check about notifying only the list of authorized? - - // val respMsg = buildRemovePresentationPodEvtMsg( - // liveMeeting.props.meetingProp.intId, - // ownerId, pod.id - // ) - // bus.outGW.send(respMsg) - // - // log.warning("RemovePresentationPodPubMsgHdlr new podId=" + pod.id) - // - // val pods = state.presentationPodManager.removePod(pod) - // state.update(pods) - } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPubMsgHdlr.scala index 64cb5c37fd49c739ac327c071ed57f639f3cf636..7894a2a0f489753cab071e08865e95133355eaba 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/RemovePresentationPubMsgHdlr.scala @@ -1,6 +1,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.LiveMeeting @@ -13,36 +14,42 @@ trait RemovePresentationPubMsgHdlr { liveMeeting: LiveMeeting, bus: MessageBus ): MeetingState2x = { - def broadcastRemovePresentationEvtMsg(podId: String, userId: String, presentationId: String): Unit = { - val routing = Routing.addMsgToClientRouting( - MessageTypes.BROADCAST_TO_MEETING, - liveMeeting.props.meetingProp.intId, userId - ) - val envelope = BbbCoreEnvelope(RemovePresentationEvtMsg.NAME, routing) - val header = BbbClientMsgHeader(RemovePresentationEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId) - - val body = RemovePresentationEvtMsgBody(podId, presentationId) - val event = RemovePresentationEvtMsg(header, body) - val msgEvent = BbbCommonEnvCoreMsg(envelope, event) - bus.outGW.send(msgEvent) - } - - val podId = msg.body.podId - val presentationId = msg.body.presentationId - - val newState = for { - pod <- PresentationPodsApp.getPresentationPod(state, podId) - } yield { - broadcastRemovePresentationEvtMsg(pod.id, msg.header.userId, presentationId) - - val pods = state.presentationPodManager.removePresentationInPod(pod.id, presentationId) - log.warning("_____ RemovePresentationPubMsgHdlr _ " + pods.printPods()) - state.update(pods) - } - - newState match { - case Some(ns) => ns - case None => state + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.MOD_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to remove presentation from meeting." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def broadcastRemovePresentationEvtMsg(podId: String, userId: String, presentationId: String): Unit = { + val routing = Routing.addMsgToClientRouting( + MessageTypes.BROADCAST_TO_MEETING, + liveMeeting.props.meetingProp.intId, userId + ) + val envelope = BbbCoreEnvelope(RemovePresentationEvtMsg.NAME, routing) + val header = BbbClientMsgHeader(RemovePresentationEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId) + + val body = RemovePresentationEvtMsgBody(podId, presentationId) + val event = RemovePresentationEvtMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, event) + bus.outGW.send(msgEvent) + } + + val podId = msg.body.podId + val presentationId = msg.body.presentationId + + val newState = for { + pod <- PresentationPodsApp.getPresentationPod(state, podId) + } yield { + broadcastRemovePresentationEvtMsg(pod.id, msg.header.userId, presentationId) + + val pods = state.presentationPodManager.removePresentationInPod(pod.id, presentationId) + state.update(pods) + } + + newState match { + case Some(ns) => ns + case None => state + } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPagePubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPagePubMsgHdlr.scala old mode 100644 new mode 100755 index b7bb49a520febf9a7c42025bcf0e6d3b47273192..fb26b89b7bbdb9277158b80f7954cb37fcb2d741 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPagePubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPagePubMsgHdlr.scala @@ -1,6 +1,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.LiveMeeting @@ -15,44 +16,52 @@ trait SetCurrentPagePubMsgHdlr { liveMeeting: LiveMeeting, bus: MessageBus ): MeetingState2x = { - def broadcastSetCurrentPageEvtMsg(podId: String, presentationId: String, pageId: String, userId: String): Unit = { - val routing = Routing.addMsgToClientRouting( - MessageTypes.BROADCAST_TO_MEETING, - liveMeeting.props.meetingProp.intId, userId - ) - val envelope = BbbCoreEnvelope(SetCurrentPageEvtMsg.NAME, routing) - val header = BbbClientMsgHeader(SetCurrentPageEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId) - - val body = SetCurrentPageEvtMsgBody(podId, presentationId, pageId) - val event = SetCurrentPageEvtMsg(header, body) - val msgEvent = BbbCommonEnvCoreMsg(envelope, event) - bus.outGW.send(msgEvent) - } + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to set presentation page." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def broadcastSetCurrentPageEvtMsg(podId: String, presentationId: String, pageId: String, userId: String): Unit = { + val routing = Routing.addMsgToClientRouting( + MessageTypes.BROADCAST_TO_MEETING, + liveMeeting.props.meetingProp.intId, userId + ) + val envelope = BbbCoreEnvelope(SetCurrentPageEvtMsg.NAME, routing) + val header = BbbClientMsgHeader(SetCurrentPageEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId) + + val body = SetCurrentPageEvtMsgBody(podId, presentationId, pageId) + val event = SetCurrentPageEvtMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, event) + bus.outGW.send(msgEvent) + } - val podId = msg.body.podId - val userId = msg.header.userId - val presentationId = msg.body.presentationId - val pageId = msg.body.pageId + val podId = msg.body.podId + val userId = msg.header.userId + val presentationId = msg.body.presentationId + val pageId = msg.body.pageId - val newState = for { - pod <- PresentationPodsApp.getPresentationPod(state, podId) - presentationToModify <- pod.getPresentation(presentationId) - updatedPod <- pod.setCurrentPage(presentationId, pageId) - } yield { + val newState = for { + pod <- PresentationPodsApp.getPresentationPod(state, podId) + presentationToModify <- pod.getPresentation(presentationId) + updatedPod <- pod.setCurrentPage(presentationId, pageId) + } yield { - if (Users2x.userIsInPresenterGroup(liveMeeting.users2x, userId) || userId.equals(pod.ownerId)) { - broadcastSetCurrentPageEvtMsg(pod.id, presentationId, pageId, userId) + if (Users2x.userIsInPresenterGroup(liveMeeting.users2x, userId) || userId.equals(pod.ownerId)) { + broadcastSetCurrentPageEvtMsg(pod.id, presentationId, pageId, userId) - val pods = state.presentationPodManager.addPod(updatedPod) - state.update(pods) - } else { - state + val pods = state.presentationPodManager.addPod(updatedPod) + state.update(pods) + } else { + state + } } - } - newState match { - case Some(ns) => ns - case None => state + newState match { + case Some(ns) => ns + case None => state + } } + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPresentationPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPresentationPubMsgHdlr.scala old mode 100644 new mode 100755 index 52431df0ef84932cd4ac834b5a885e0adce4986e..5a166aaf1f2ee7dc2bb302ee328dfd53806aeb38 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPresentationPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetCurrentPresentationPubMsgHdlr.scala @@ -1,6 +1,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.LiveMeeting @@ -13,34 +14,39 @@ trait SetCurrentPresentationPubMsgHdlr { liveMeeting: LiveMeeting, bus: MessageBus ): MeetingState2x = { - def broadcastSetCurrentPresentationEvent(podId: String, userId: String, presentationId: String): Unit = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId) - val envelope = BbbCoreEnvelope(SetCurrentPresentationEvtMsg.NAME, routing) - val header = BbbClientMsgHeader(SetCurrentPresentationEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId) - - val body = SetCurrentPresentationEvtMsgBody(podId, presentationId) - val event = SetCurrentPresentationEvtMsg(header, body) - val msgEvent = BbbCommonEnvCoreMsg(envelope, event) - bus.outGW.send(msgEvent) - } - - val podId = msg.body.podId - val presId = msg.body.presentationId - - val newState = for { - updatedPod <- PresentationPodsApp.setCurrentPresentationInPod(state, podId, presId) - } yield { - broadcastSetCurrentPresentationEvent(podId, msg.header.userId, presId) - - log.warning("_____ SetCurrentPresentationPubMsgHdlr before_ " + state.presentationPodManager.printPods()) - val pods = state.presentationPodManager.addPod(updatedPod) - log.warning("_____ SetCurrentPresentationPubMsgHdlr after_ " + pods.printPods()) - state.update(pods) - } - - newState match { - case Some(ns) => ns - case None => state + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to set presentation page." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def broadcastSetCurrentPresentationEvent(podId: String, userId: String, presentationId: String): Unit = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, userId) + val envelope = BbbCoreEnvelope(SetCurrentPresentationEvtMsg.NAME, routing) + val header = BbbClientMsgHeader(SetCurrentPresentationEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId) + + val body = SetCurrentPresentationEvtMsgBody(podId, presentationId) + val event = SetCurrentPresentationEvtMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, event) + bus.outGW.send(msgEvent) + } + + val podId = msg.body.podId + val presId = msg.body.presentationId + + val newState = for { + updatedPod <- PresentationPodsApp.setCurrentPresentationInPod(state, podId, presId) + } yield { + broadcastSetCurrentPresentationEvent(podId, msg.header.userId, presId) + + val pods = state.presentationPodManager.addPod(updatedPod) + state.update(pods) + } + + newState match { + case Some(ns) => ns + case None => state + } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetPresenterInPodReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetPresenterInPodReqMsgHdlr.scala old mode 100644 new mode 100755 index 4828a0fbbaf65fa94a78b0b05b1a611a8769f2be..0e3b63ee45b296945d419ff09b32aa8b70072f16 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetPresenterInPodReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetPresenterInPodReqMsgHdlr.scala @@ -1,6 +1,7 @@ package org.bigbluebutton.core.apps.presentationpod import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.PermissionCheck import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.running.LiveMeeting @@ -14,44 +15,52 @@ trait SetPresenterInPodReqMsgHdlr { liveMeeting: LiveMeeting, bus: MessageBus ): MeetingState2x = { - def broadcastSetPresenterInPodRespMsg(podId: String, prevPresenterId: String, nextPresenterId: String, requesterId: String): Unit = { - val routing = Routing.addMsgToClientRouting( - MessageTypes.BROADCAST_TO_MEETING, - liveMeeting.props.meetingProp.intId, requesterId - ) - val envelope = BbbCoreEnvelope(SetPresenterInPodRespMsg.NAME, routing) - val header = BbbClientMsgHeader(SetPresenterInPodRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId) - - val body = SetPresenterInPodRespMsgBody(podId, prevPresenterId, nextPresenterId) - val event = SetPresenterInPodRespMsg(header, body) - val msgEvent = BbbCommonEnvCoreMsg(envelope, event) - bus.outGW.send(msgEvent) - } + if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) { + val meetingId = liveMeeting.props.meetingProp.intId + val reason = "No permission to set presenter in presentation." + PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW) + state + } else { + def broadcastSetPresenterInPodRespMsg(podId: String, prevPresenterId: String, nextPresenterId: String, requesterId: String): Unit = { + val routing = Routing.addMsgToClientRouting( + MessageTypes.BROADCAST_TO_MEETING, + liveMeeting.props.meetingProp.intId, requesterId + ) + val envelope = BbbCoreEnvelope(SetPresenterInPodRespMsg.NAME, routing) + val header = BbbClientMsgHeader(SetPresenterInPodRespMsg.NAME, liveMeeting.props.meetingProp.intId, requesterId) + + val body = SetPresenterInPodRespMsgBody(podId, prevPresenterId, nextPresenterId) + val event = SetPresenterInPodRespMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, event) + bus.outGW.send(msgEvent) + } - val podId: String = msg.body.podId - val requesterId: String = msg.header.userId - val nextPresenterId: String = msg.body.nextPresenterId - val prevPresenterId: String = msg.body.prevPresenterId + val podId: String = msg.body.podId + val requesterId: String = msg.header.userId + val nextPresenterId: String = msg.body.nextPresenterId + val prevPresenterId: String = msg.body.prevPresenterId - val newState = for { - pod <- PresentationPodsApp.getPresentationPod(state, podId) - } yield { + val newState = for { + pod <- PresentationPodsApp.getPresentationPod(state, podId) + } yield { - if (Users2x.userIsInPresenterGroup(liveMeeting.users2x, requesterId) || requesterId.equals(pod.ownerId)) { - val updatedPod = pod.setCurrentPresenter(nextPresenterId) + if (Users2x.userIsInPresenterGroup(liveMeeting.users2x, requesterId) || requesterId.equals(pod.ownerId)) { + val updatedPod = pod.setCurrentPresenter(nextPresenterId) - broadcastSetPresenterInPodRespMsg(pod.id, prevPresenterId, nextPresenterId, requesterId) + broadcastSetPresenterInPodRespMsg(pod.id, prevPresenterId, nextPresenterId, requesterId) - val pods = state.presentationPodManager.addPod(updatedPod) - state.update(pods) - } else { - state + val pods = state.presentationPodManager.addPod(updatedPod) + state.update(pods) + } else { + state + } } - } - newState match { - case Some(ns) => ns - case None => state + newState match { + case Some(ns) => ns + case None => state + } } + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/AddUserToPresenterGroupCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/AddUserToPresenterGroupCmdMsgHdlr.scala index 8d664ebc520bd1e3b130b6b2034a7cf12e885939..c73a32f7f251c4b8bc59ff7a513b496ed6354e4c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/AddUserToPresenterGroupCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/AddUserToPresenterGroupCmdMsgHdlr.scala @@ -13,17 +13,6 @@ trait AddUserToPresenterGroupCmdMsgHdlr { def handleAddUserToPresenterGroupCmdMsg(msg: AddUserToPresenterGroupCmdMsg) { - def broadcastAddUserToPresenterGroup(meetingId: String, userId: String, requesterId: String): Unit = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) - val envelope = BbbCoreEnvelope(UserAddedToPresenterGroupEvtMsg.NAME, routing) - val header = BbbClientMsgHeader(UserAddedToPresenterGroupEvtMsg.NAME, meetingId, userId) - val body = UserAddedToPresenterGroupEvtMsgBody(userId, requesterId) - val event = UserAddedToPresenterGroupEvtMsg(header, body) - val msgEvent = BbbCommonEnvCoreMsg(envelope, event) - - outGW.send(msgEvent) - } - if (applyPermissionCheck && !PermissionCheck.isAllowed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) { val meetingId = liveMeeting.props.meetingProp.intId val reason = "No permission to add user to presenter group." @@ -36,8 +25,7 @@ trait AddUserToPresenterGroupCmdMsgHdlr { requester <- Users2x.findWithIntId(liveMeeting.users2x, requesterId) } yield { if (requester.role == Roles.MODERATOR_ROLE) { - Users2x.addUserToPresenterGroup(liveMeeting.users2x, userId) - broadcastAddUserToPresenterGroup(liveMeeting.props.meetingProp.intId, userId, requesterId) + UsersApp.addUserToPresenterGroup(liveMeeting, outGW, userId, requesterId) } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala index a0f414b3edc317a6111cf6d1ffbd8815691fc9b0..99bf9aebf8800dcb885c8f447a2cb970fa701354 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala @@ -2,9 +2,35 @@ package org.bigbluebutton.core.apps.users import akka.actor.ActorContext import akka.event.Logging +import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.bus.InternalEventBus +import org.bigbluebutton.core.models.{ Roles, Users2x } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } +object UsersApp { + def broadcastAddUserToPresenterGroup(meetingId: String, userId: String, requesterId: String, + outGW: OutMsgRouter): Unit = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) + val envelope = BbbCoreEnvelope(UserAddedToPresenterGroupEvtMsg.NAME, routing) + val header = BbbClientMsgHeader(UserAddedToPresenterGroupEvtMsg.NAME, meetingId, userId) + val body = UserAddedToPresenterGroupEvtMsgBody(userId, requesterId) + val event = UserAddedToPresenterGroupEvtMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, event) + + outGW.send(msgEvent) + } + + def addUserToPresenterGroup(liveMeeting: LiveMeeting, outGW: OutMsgRouter, + userId: String, requesterId: String): Unit = { + Users2x.addUserToPresenterGroup(liveMeeting.users2x, userId) + UsersApp.broadcastAddUserToPresenterGroup( + liveMeeting.props.meetingProp.intId, + userId, requesterId, outGW + ) + } + +} + class UsersApp( val liveMeeting: LiveMeeting, val outGW: OutMsgRouter, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala index c730c55e5b49a010dc9dcc186a60d557934d7063..6f6782535008c31d386c1f3d16f9db2718e8afa5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala @@ -297,6 +297,9 @@ class ReceivedJsonMsgHandlerActor( case CreateGroupChatReqMsg.NAME => routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode) + case ValidateConnAuthTokenSysMsg.NAME => + routeGenericMsg[ValidateConnAuthTokenSysMsg](envelope, jsonNode) + case _ => log.error("Cannot route envelope name " + envelope.name) // do nothing 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 328f529965fc31f96a16ffc3cc6e86218aa5a20c..3a5bf819779b0f1982e8477242dfd4525aa9f591 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 @@ -3,6 +3,7 @@ package org.bigbluebutton.core.running import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, EndBreakoutRoomInternalMsg } +import org.bigbluebutton.core.apps.users.UsersApp import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus } import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.models._ @@ -54,6 +55,11 @@ trait HandlerHelpers extends SystemConfiguration { if (!Users2x.hasPresenter(liveMeeting.users2x)) { automaticallyAssignPresenter(outGW, liveMeeting) } + + if (newUser.role == Roles.MODERATOR_ROLE) { + UsersApp.addUserToPresenterGroup(liveMeeting, outGW, newUser.intId, newUser.intId) + } + state.update(state.expiryTracker.setUserHasJoined()) case None => state 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 d36b2aac9afb7dcf844233bb0db3ec209e16abc7..0c0933acb21b440af35b3601c0ef887eb73aa0de 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 @@ -1,6 +1,7 @@ package org.bigbluebutton.core.running import java.io.{ PrintWriter, StringWriter } + import akka.actor._ import akka.actor.SupervisorStrategy.Resume import org.bigbluebutton.core.apps.groupchats.{ GroupChatApp, GroupChatHdlrs } @@ -28,10 +29,11 @@ import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.apps.breakout._ import org.bigbluebutton.core.apps.polls._ import org.bigbluebutton.core.apps.voice._ + import scala.concurrent.duration._ import org.bigbluebutton.core2.testdata.FakeTestData import org.bigbluebutton.core.apps.layout.LayoutApp2x -import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr +import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr } import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr import org.bigbluebutton.core2.message.senders.MsgBuilder @@ -76,7 +78,8 @@ class MeetingActor( with SendBreakoutTimeRemainingMsgHdlr with ChangeLockSettingsInMeetingCmdMsgHdlr with SyncGetMeetingInfoRespMsgHdlr - with ClientToServerLatencyTracerMsgHdlr { + with ClientToServerLatencyTracerMsgHdlr + with ValidateConnAuthTokenSysMsgHdlr { override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { case e: Exception => { @@ -342,6 +345,8 @@ class MeetingActor( case m: GetGroupChatsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus) case m: SendGroupChatMessageMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus) + case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m) + case _ => log.warning("***** Cannot handle " + msg.envelope.name) } } @@ -414,7 +419,7 @@ class MeetingActor( val elapsedInMs = now - lastRecBreakSentOn val elapsedInMin = TimeUtil.millisToMinutes(elapsedInMs) - if (elapsedInMin > 1) { + if (elapsedInMin > recordingChapterBreakLenghtInMinutes) { lastRecBreakSentOn = now val event = MsgBuilder.buildRecordingChapterBreakSysMsg(props.meetingProp.intId, TimeUtil.timeNowInMs()) outGW.send(event) 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 8ddf5eb3bd669c12c721e93b672301d0a7a3e5b8..ef81b67e1a9b320a5c911d5b60360a3364c66c1d 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 @@ -63,6 +63,16 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } + def buildValidateConnAuthTokenSysRespMsg(meetingId: String, userId: String, authToken: String, + authzed: Boolean, conn: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId) + val envelope = BbbCoreEnvelope(ValidateConnAuthTokenSysRespMsg.NAME, routing) + val header = BbbCoreHeaderWithMeetingId(ValidateConnAuthTokenSysRespMsg.NAME, meetingId) + val body = ValidateConnAuthTokenSysRespMsgBody(meetingId, userId, authToken, conn, authzed) + val event = ValidateConnAuthTokenSysRespMsg(header, body) + BbbCommonEnvCoreMsg(envelope, event) + } + def buildValidateAuthTokenRespMsg(meetingId: String, userId: String, authToken: String, valid: Boolean, waitForApproval: Boolean): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId) diff --git a/bbb-apps-common/build.sbt b/bbb-apps-common/build.sbt index 79190720d8e210d18dc943fb6c40ad970a7af4b1..355f55b53d4f3ed10d5cbc025391502798dd19d0 100755 --- a/bbb-apps-common/build.sbt +++ b/bbb-apps-common/build.sbt @@ -3,7 +3,7 @@ name := "bbb-apps-common" organization := "org.bigbluebutton" -version := "0.0.2" +version := "0.0.3-SNAPSHOT" scalaVersion := "2.12.2" diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientGWApplication.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientGWApplication.scala index db86d661d7884f0aed168bb82e9e2f85b6d66a63..c25fd9b5bcdbf82c4c5c7c5b50634602295b309e 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientGWApplication.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientGWApplication.scala @@ -73,22 +73,22 @@ class ClientGWApplication(val msgToClientGW: MsgToClientGW, */ def connect(connInfo: ConnInfo): Unit = { - log.debug("**** ClientGWApplication connect " + connInfo) + //log.debug("**** ClientGWApplication connect " + connInfo) msgFromClientEventBus.publish(MsgFromClientBusMsg(fromClientChannel, new ConnectMsg(connInfo))) } def disconnect(connInfo: ConnInfo): Unit = { - log.debug("**** ClientGWApplication disconnect " + connInfo) + //log.debug("**** ClientGWApplication disconnect " + connInfo) msgFromClientEventBus.publish(MsgFromClientBusMsg(fromClientChannel, new DisconnectMsg(connInfo))) } def handleMsgFromClient(connInfo: ConnInfo, json: String): Unit = { - log.debug("**** ClientGWApplication handleMsgFromClient " + json) + //log.debug("**** ClientGWApplication handleMsgFromClient " + json) msgFromClientEventBus.publish(MsgFromClientBusMsg(fromClientChannel, new MsgFromClientMsg(connInfo, json))) } def send(channel: String, json: String): Unit = { - log.debug("Sending message {}", json) + //log.debug("Sending message {}", json) jsonMsgToAkkaAppsBus.publish(JsonMsgToAkkaAppsBusMsg(toAkkaAppsJsonChannel, new JsonMsgToSendToAkkaApps(channel, json))) } diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ReceivedJsonMsgHdlrActor.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ReceivedJsonMsgHdlrActor.scala index 83b4f0e88123bb30a8cb81629008cd20b7dd57dc..d18cb333ed7e2cbd1066db5c11e61bf3e50160e8 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ReceivedJsonMsgHdlrActor.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/ReceivedJsonMsgHdlrActor.scala @@ -24,7 +24,7 @@ class ReceivedJsonMsgHdlrActor(val msgFromAkkaAppsEventBus: MsgFromAkkaAppsEvent } def handleReceivedJsonMessage(msg: JsonMsgFromAkkaApps): Unit = { - log.debug("****** Received JSON msg " + msg.data) + //log.debug("****** Received JSON msg " + msg.data) JsonUtil.fromJson[BbbCommonEnvJsNodeMsg](msg.data) match { case Success(m) => msgFromAkkaAppsEventBus.publish(MsgFromAkkaApps(fromAkkaAppsChannel, m)) case Failure(ex) => log.error("Failed to deserialize message " + ex) diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/AppsRedisSubscriberActor.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/AppsRedisSubscriberActor.scala index 51a8574cc12ba6d2af63eb9266098bcd4342bb0e..efd2335cd43fe0a9a6f9ebabba89c36920f47fe3 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/AppsRedisSubscriberActor.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/AppsRedisSubscriberActor.scala @@ -49,7 +49,7 @@ class AppsRedisSubscriberActor(jsonMsgBus: JsonMsgFromAkkaAppsBus, oldMessageEve def onMessage(message: Message) { //log.error(s"SHOULD NOT BE RECEIVING: $message") if (channels.contains(message.channel)) { - log.debug(s"RECEIVED:\n ${message.data.utf8String} \n") + //log.debug(s"RECEIVED:\n ${message.data.utf8String} \n") val receivedJsonMessage = new JsonMsgFromAkkaApps(message.channel, message.data.utf8String) jsonMsgBus.publish(JsonMsgFromAkkaAppsEvent(fromAkkaAppsJsonChannel, receivedJsonMessage)) } diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/RedisPublisher.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/RedisPublisher.scala index 97333a93aea75ef29b0c39372e19cbf7be5f2881..379b15f5dec53f025e454613d7c8b2a08bdcf042 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/RedisPublisher.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/RedisPublisher.scala @@ -17,7 +17,7 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration { redis.clientSetname("Red5AppsPub") def publish(channel: String, data: String) { - log.debug("PUBLISH TO [" + channel + "]: \n [" + data + "]") + //log.debug("PUBLISH TO [" + channel + "]: \n [" + data + "]") redis.publish(channel, ByteString(data)) } diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingActor.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingActor.scala index 1a77e0c628da1424e183c8bd174a12abd8716cf3..e002dfcf577cb592c74939de5f2ea5cd43560a0a 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingActor.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingActor.scala @@ -31,7 +31,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv } def handleConnectMsg(msg: ConnectMsg): Unit = { - log.debug("**** MeetingActor handleConnectMsg " + msg.connInfo.meetingId) + //log.debug("**** MeetingActor handleConnectMsg " + msg.connInfo.meetingId) UsersManager.findWithId(userMgr, msg.connInfo.userId) match { case Some(m) => m.actorRef forward(msg) case None => @@ -42,7 +42,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv } def handleDisconnectMsg(msg: DisconnectMsg): Unit = { - log.debug("**** MeetingActor handleDisconnectMsg " + msg.connInfo.meetingId) + //log.debug("**** MeetingActor handleDisconnectMsg " + msg.connInfo.meetingId) for { m <- UsersManager.findWithId(userMgr, msg.connInfo.userId) } yield { @@ -51,7 +51,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv } def handleMsgFromClientMsg(msg: MsgFromClientMsg):Unit = { - log.debug("**** MeetingActor handleMsgFromClient " + msg.json) + //log.debug("**** MeetingActor handleMsgFromClient " + msg.json) for { m <- UsersManager.findWithId(userMgr, msg.connInfo.userId) } yield { @@ -60,7 +60,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv } def handleBbbServerMsg(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingActor handleBbbServerMsg " + msg.envelope.name) + //log.debug("**** MeetingActor handleBbbServerMsg " + msg.envelope.name) for { msgType <- msg.envelope.routing.get("msgType") } yield { @@ -69,7 +69,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv } def handleServerMsg(msgType: String, msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingActor handleServerMsg " + msg.envelope.name) + //log.debug("**** MeetingActor handleServerMsg " + msg.envelope.name) msgType match { case MessageTypes.DIRECT => handleDirectMessage(msg) case MessageTypes.BROADCAST_TO_MEETING => handleBroadcastMessage(msg) @@ -78,18 +78,18 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv } private def forwardToUser(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingActor forwardToUser " + msg.envelope.name) + //log.debug("**** MeetingActor forwardToUser " + msg.envelope.name) for { userId <- msg.envelope.routing.get("userId") m <- UsersManager.findWithId(userMgr, userId) } yield { - log.debug("**** MeetingActor forwardToUser " + m.userId) + //log.debug("**** MeetingActor forwardToUser " + m.userId) m.actorRef forward(msg) } } def handleDirectMessage(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingActor handleDirectMessage " + msg.envelope.name) + //log.debug("**** MeetingActor handleDirectMessage " + msg.envelope.name) // In case we want to handle specific messages. We can do it here. forwardToUser(msg) } diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingManagerActor.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingManagerActor.scala index e2cde3288761b2f229e8098bb96080ef14e3fcd7..696cff1b83133c3150f6c90015b2799394fcfbd3 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingManagerActor.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingManagerActor.scala @@ -30,7 +30,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, } def handleConnectMsg(msg: ConnectMsg): Unit = { - log.debug("****** Received handleConnectMsg " + msg) + //log.debug("****** Received handleConnectMsg " + msg) MeetingManager.findWithMeetingId(meetingMgr, msg.connInfo.meetingId) match { case Some(m) => m.actorRef forward(msg) case None => @@ -41,7 +41,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, } def handleDisconnectMsg(msg: DisconnectMsg): Unit = { - log.debug("****** Received handleDisconnectMsg " + msg) + //log.debug("****** Received handleDisconnectMsg " + msg) for { m <- MeetingManager.findWithMeetingId(meetingMgr, msg.connInfo.meetingId) } yield { @@ -50,7 +50,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, } def handleMsgFromClientMsg(msg: MsgFromClientMsg):Unit = { - log.debug("**** MeetingManagerActor handleMsgFromClient " + msg.json) + //log.debug("**** MeetingManagerActor handleMsgFromClient " + msg.json) for { m <- MeetingManager.findWithMeetingId(meetingMgr, msg.connInfo.meetingId) } yield { @@ -59,7 +59,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, } def handleBbbServerMsg(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingManagerActor handleBbbServerMsg " + msg.envelope.name) + //log.debug("**** MeetingManagerActor handleBbbServerMsg " + msg.envelope.name) for { msgType <- msg.envelope.routing.get("msgType") } yield { @@ -68,7 +68,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, } def handleServerMsg(msgType: String, msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingManagerActor handleServerMsg " + msg.envelope.name) + //log.debug("**** MeetingManagerActor handleServerMsg " + msg.envelope.name) msgType match { case MessageTypes.DIRECT => handleDirectMessage(msg) case MessageTypes.BROADCAST_TO_MEETING => handleBroadcastMessage(msg) @@ -78,10 +78,10 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, private def forwardToMeeting(msg: BbbCommonEnvJsNodeMsg): Unit = { msg.envelope.routing.get("meetingId") match { - case Some(meetingId2) => log.debug("**** MeetingManagerActor forwardToMeeting. Found " + meetingId2) + case Some(meetingId2) => //log.debug("**** MeetingManagerActor forwardToMeeting. Found " + meetingId2) MeetingManager.findWithMeetingId(meetingMgr, meetingId2) match { - case Some(meetingId2) => log.debug("**** MeetingManagerActor forwardToMeeting. Found " + meetingId2.meetingId) - case None => log.debug("**** MeetingManagerActor forwardToMeeting. Could not find meetingId") + case Some(meetingId2) => //log.debug("**** MeetingManagerActor forwardToMeeting. Found " + meetingId2.meetingId) + case None => //log.debug("**** MeetingManagerActor forwardToMeeting. Could not find meetingId") } case None => log.debug("**** MeetingManagerActor forwardToMeeting. Could not find meetingId") } @@ -92,25 +92,25 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, meetingId <- msg.envelope.routing.get("meetingId") m <- MeetingManager.findWithMeetingId(meetingMgr, meetingId) } yield { - log.debug("**** MeetingManagerActor forwardToMeeting. " + m.meetingId) + //log.debug("**** MeetingManagerActor forwardToMeeting. " + m.meetingId) m.actorRef forward(msg) } } def handleDirectMessage(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingManagerActor handleDirectMessage " + msg.envelope.name) + //log.debug("**** MeetingManagerActor handleDirectMessage " + msg.envelope.name) // In case we want to handle specific message. We can do it here. forwardToMeeting(msg) } def handleBroadcastMessage(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingManagerActor handleBroadcastMessage " + msg.envelope.name) + // log.debug("**** MeetingManagerActor handleBroadcastMessage " + msg.envelope.name) // In case we want to handle specific message. We can do it here. forwardToMeeting(msg) } def handleSystemMessage(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** MeetingManagerActor handleSystemMessage " + msg.envelope.name) + //log.debug("**** MeetingManagerActor handleSystemMessage " + msg.envelope.name) // In case we want to handle specific message. We can do it here. forwardToMeeting(msg) } diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/UserActor.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/UserActor.scala index f8c483abf65f393bc8dd2d5f5ecc99b4193207bb..bcd29a40fbc57d0270b25cc6e10e5c43bce02afb 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/UserActor.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/UserActor.scala @@ -119,7 +119,7 @@ class UserActor(val userId: String, } def handleBbbServerMsg(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** UserActor handleBbbServerMsg " + msg) + //log.debug("**** UserActor handleBbbServerMsg " + msg) for { msgType <- msg.envelope.routing.get("msgType") } yield { @@ -128,7 +128,7 @@ class UserActor(val userId: String, } def handleServerMsg(msgType: String, msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("**** UserActor handleServerMsg " + msg) + // log.debug("**** UserActor handleServerMsg " + msg) msgType match { case MessageTypes.DIRECT => handleDirectMessage(msg) case MessageTypes.BROADCAST_TO_MEETING => handleBroadcastMessage(msg) @@ -137,7 +137,7 @@ class UserActor(val userId: String, } private def forwardToUser(msg: BbbCommonEnvJsNodeMsg): Unit = { - log.debug("UserActor forwardToUser. Forwarding to connection. " + msg) + //log.debug("UserActor forwardToUser. Forwarding to connection. " + msg) for { conn <- Connections.findActiveConnection(conns) } yield { diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala index fb1dc6af3b30b0ebea20a2e7ebe83a09c8c83f40..333c95a5d87c98bdafde054c905c4eb1077bf0f8 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala @@ -128,4 +128,15 @@ case class CheckAlivePongSysMsgBody(system: String, timestamp: Long) object RecordingChapterBreakSysMsg { val NAME = "RecordingChapterBreakSysMsg" } case class RecordingChapterBreakSysMsg(header: BbbCoreHeaderWithMeetingId, body: RecordingChapterBreakSysMsgBody) extends BbbCoreMsg -case class RecordingChapterBreakSysMsgBody(meetingId: String, timestamp: Long) \ No newline at end of file +case class RecordingChapterBreakSysMsgBody(meetingId: String, timestamp: Long) + +object ValidateConnAuthTokenSysMsg { val NAME = "ValidateConnAuthTokenSysMsg" } +case class ValidateConnAuthTokenSysMsg(header: BbbClientMsgHeader, + body: ValidateConnAuthTokenSysMsgBody) extends StandardMsg +case class ValidateConnAuthTokenSysMsgBody(meetingId: String, userId: String, authToken: String, conn: String) + +object ValidateConnAuthTokenSysRespMsg { val NAME = "ValidateConnAuthTokenSysRespMsg" } +case class ValidateConnAuthTokenSysRespMsg(header: BbbCoreHeaderWithMeetingId, + body: ValidateConnAuthTokenSysRespMsgBody) extends BbbCoreMsg +case class ValidateConnAuthTokenSysRespMsgBody(meetingId: String, userId: String, + authToken: String, conn: String, authzed: Boolean) \ No newline at end of file diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/Meeting.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/Meeting.java new file mode 100755 index 0000000000000000000000000000000000000000..9a932c938b3d6ba9fcb029610b6d147bb0fad2a9 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/Meeting.java @@ -0,0 +1,44 @@ +package org.bigbluebutton.app.screenshare; + +import java.util.HashMap; +import java.util.Map; + +public class Meeting { + public final String id; + + private Map<String, VideoStream> videoStreams = new HashMap<String, VideoStream>(); + + public Meeting(String id) { + this.id = id; + } + + public synchronized void addStream(VideoStream stream) { + videoStreams.put(stream.getStreamId(), stream); + } + + public synchronized void removeStream(String streamId) { + VideoStream vs = videoStreams.remove(streamId); + } + + public synchronized void streamBroadcastClose(String streamId) { + VideoStream vs = videoStreams.remove(streamId); + if (vs != null) { + vs.streamBroadcastClose(); + } + } + + public synchronized boolean hasVideoStreams() { + return !videoStreams.isEmpty(); + } + + public synchronized void stopStartRecording(String streamId) { + VideoStream vs = videoStreams.get(streamId); + if (vs != null) vs.stopStartRecording(); + } + + public synchronized void stopStartAllRecordings() { + for (VideoStream vs : videoStreams.values()) { + stopStartRecording(vs.getStreamId()); + } + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/MeetingManager.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/MeetingManager.java new file mode 100755 index 0000000000000000000000000000000000000000..76861013ed9c7767457fc484986129263e08ce49 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/MeetingManager.java @@ -0,0 +1,53 @@ +package org.bigbluebutton.app.screenshare; + +import java.util.HashMap; +import java.util.Map; + +public class MeetingManager { + + private Map<String, Meeting> meetings = new HashMap<String, Meeting>(); + + private void add(Meeting m) { + meetings.put(m.id, m); + } + + private void remove(String id) { + Meeting m = meetings.remove(id); + } + + public void addStream(String meetingId, VideoStream vs) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.addStream(vs); + } else { + Meeting nm = new Meeting(meetingId); + nm.addStream(vs); + add(nm); + } + } + + public void removeStream(String meetingId, String streamId) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.removeStream(streamId); + } + } + + public void streamBroadcastClose(String meetingId, String streamId) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.streamBroadcastClose(streamId); + if (!m.hasVideoStreams()) { + remove(m.id); + } + } + } + + public synchronized void stopStartAllRecordings(String meetingId) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.stopStartAllRecordings(); + } + } +} + diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStream.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStream.java new file mode 100755 index 0000000000000000000000000000000000000000..0ff5b30d93b538347494354f922b100d841a16cf --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStream.java @@ -0,0 +1,65 @@ +package org.bigbluebutton.app.screenshare; + +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.api.IConnection; +import org.red5.server.api.Red5; +import org.red5.server.api.scope.IScope; +import org.red5.server.api.stream.IBroadcastStream; +import org.red5.server.stream.ClientBroadcastStream; +import org.slf4j.Logger; + +public class VideoStream { + private static Logger log = Red5LoggerFactory.getLogger(VideoStream.class, "screenshare"); + + private VideoStreamListener videoStreamListener; + private IScope scope; + private String streamId; + private IBroadcastStream stream; + private String recordingStreamName; + private ClientBroadcastStream cstream; + + public VideoStream(IBroadcastStream stream, VideoStreamListener videoStreamListener, ClientBroadcastStream cstream) { + this.stream = stream; + this.videoStreamListener = videoStreamListener; + stream.addStreamListener(videoStreamListener); + this.cstream = cstream; + } + + public String getStreamId() { + return streamId; + } + + public synchronized void startRecording() { + long now = System.currentTimeMillis(); + recordingStreamName = stream.getPublishedName() + "-" + now; + try { + log.info("Recording stream " + recordingStreamName); + videoStreamListener.setStreamId(recordingStreamName); + cstream.saveAs(recordingStreamName, false); + } catch (Exception e) { + log.error("ERROR while recording stream " + e.getMessage()); + e.printStackTrace(); + } + } + + public synchronized void stopRecording() { + if (cstream.isRecording()) { + cstream.stopRecording(); + videoStreamListener.stopRecording(); + videoStreamListener.reset(); + } + } + + public synchronized void stopStartRecording() { + stopRecording(); + videoStreamListener.reset(); + startRecording(); + } + + public synchronized void streamBroadcastClose() { + stopRecording(); + + videoStreamListener.streamStopped(); + stream.removeStreamListener(videoStreamListener); + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStreamListener.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStreamListener.java new file mode 100755 index 0000000000000000000000000000000000000000..49d461c4241d1ac2d81e47c2bd978c3ebe2f4ea2 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStreamListener.java @@ -0,0 +1,236 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * <p> + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * <p> + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 3.0 of the License, or (at your option) any later + * version. + * <p> + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * <p> + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + */ +package org.bigbluebutton.app.screenshare; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.mina.core.buffer.IoBuffer; +import org.red5.server.api.scheduling.IScheduledJob; +import org.red5.server.api.scheduling.ISchedulingService; +import org.red5.server.api.scope.IScope; +import org.red5.server.api.stream.IBroadcastStream; +import org.red5.server.api.stream.IStreamListener; +import org.red5.server.api.stream.IStreamPacket; +import org.red5.server.net.rtmp.event.VideoData; +import org.red5.server.scheduling.QuartzSchedulingService; +import org.slf4j.Logger; +import org.red5.logging.Red5LoggerFactory; + +import com.google.gson.Gson; + +/** + * Class to listen for the first video packet of the webcam. + * We need to listen for the first packet and send a startWebcamEvent. + * The reason is that when starting the webcam, sometimes Flash Player + * needs to prompt the user for permission to access the webcam. However, + * while waiting for the user to click OK to the prompt, Red5 has already + * called the startBroadcast method which we take as the start of the recording. + * When the user finally clicks OK, the packets then start to flow through. + * This introduces a delay of when we assume the start of the recording and + * the webcam actually publishes video packets. When we do the ingest and + * processing of the video and multiplex the audio, the video and audio will + * be un-synched by at least this amount of delay. + * @author Richard Alam + * + */ +public class VideoStreamListener implements IStreamListener { + private static final Logger log = Red5LoggerFactory.getLogger(VideoStreamListener.class, "video"); + + private EventRecordingService recordingService; + private volatile boolean firstPacketReceived = false; + + // Maximum time between video packets + private int videoTimeout = 10000; + private long firstPacketTime = 0L; + private long packetCount = 0L; + + // Last time video was received, not video timestamp + private long lastVideoTime; + + private String recordingDir; + + // Stream being observed + private String streamId; + + // if this stream is recorded or not + private boolean record; + + // Scheduler + private QuartzSchedulingService scheduler; + + // Event queue worker job name + private String timeoutJobName; + + private volatile boolean publishing = false; + + private volatile boolean streamPaused = false; + + private String meetingId; + + private long recordingStartTime; + private String filename; + + public VideoStreamListener(String meetingId, String streamId, Boolean record, + String recordingDir, int packetTimeout, + QuartzSchedulingService scheduler, + EventRecordingService recordingService) { + this.meetingId = meetingId; + this.streamId = streamId; + this.record = record; + this.videoTimeout = packetTimeout; + this.recordingDir = recordingDir; + this.scheduler = scheduler; + this.recordingService = recordingService; + } + + private Long genTimestamp() { + return TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + } + + public void reset() { + firstPacketReceived = false; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + @Override + public void packetReceived(IBroadcastStream stream, IStreamPacket packet) { + IoBuffer buf = packet.getData(); + if (buf != null) + buf.rewind(); + + if (buf == null || buf.remaining() == 0) { + return; + } + + if (packet instanceof VideoData) { + // keep track of last time video was received + lastVideoTime = System.currentTimeMillis(); + packetCount++; + + if (!firstPacketReceived) { + firstPacketReceived = true; + publishing = true; + firstPacketTime = lastVideoTime; + + // start the worker to monitor if we are still receiving video packets + timeoutJobName = scheduler.addScheduledJob(videoTimeout, new TimeoutJob()); + + if (record) { + recordingStartTime = System.currentTimeMillis(); + filename = recordingDir; + if (!filename.endsWith("/")) { + filename.concat("/"); + } + + filename = filename.concat(meetingId).concat("/").concat(streamId).concat(".flv"); + recordingStartTime = System.currentTimeMillis(); + Map<String, String> event = new HashMap<String, String>(); + event.put("module", "Deskshare"); + event.put("timestamp", genTimestamp().toString()); + event.put("meetingId", meetingId); + event.put("file", filename); + event.put("stream", streamId); + event.put("eventName", "DeskshareStartedEvent"); + + recordingService.record(meetingId, event); + } + } + + + if (streamPaused) { + streamPaused = false; + long now = System.currentTimeMillis(); + long numSeconds = (now - lastVideoTime) / 1000; + + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", meetingId); + logData.put("stream", streamId); + logData.put("packetCount", packetCount); + logData.put("publishing", publishing); + logData.put("pausedFor (sec)", numSeconds); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Screenshare stream restarted. data={}", logStr); + } + + } + } + + public void stopRecording() { + if (record) { + long publishDuration = (System.currentTimeMillis() - recordingStartTime) / 1000; + + Map<String, String> event = new HashMap<String, String>(); + event.put("module", "Deskshare"); + event.put("timestamp", genTimestamp().toString()); + event.put("meetingId", meetingId); + event.put("stream", streamId); + event.put("file", filename); + event.put("duration", new Long(publishDuration).toString()); + event.put("eventName", "DeskshareStoppedEvent"); + recordingService.record(meetingId, event); + } + } + + public void streamStopped() { + this.publishing = false; + } + + private class TimeoutJob implements IScheduledJob { + private boolean streamStopped = false; + + public void execute(ISchedulingService service) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", meetingId); + logData.put("stream", streamId); + logData.put("packetCount", packetCount); + logData.put("publishing", publishing); + + Gson gson = new Gson(); + + long now = System.currentTimeMillis(); + if ((now - lastVideoTime) > videoTimeout && !streamPaused) { + streamPaused = true; + long numSeconds = (now - lastVideoTime) / 1000; + + logData.put("lastPacketTime (sec)", numSeconds); + + String logStr = gson.toJson(logData); + + log.warn("Screenshare packet timeout. data={}", logStr); + + } + + String logStr = gson.toJson(logData); + if (!publishing) { + log.warn("Removing scheduled job. data={}", logStr); + // remove the scheduled job + scheduler.removeScheduledJob(timeoutJobName); + } + } + + } + +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/RecordChapterBreakMessage.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/RecordChapterBreakMessage.java new file mode 100755 index 0000000000000000000000000000000000000000..afaf2c61eec3961874e6e00c8b8992f5d1c2b3d5 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/RecordChapterBreakMessage.java @@ -0,0 +1,11 @@ +package org.bigbluebutton.app.screenshare.events; + +public class RecordChapterBreakMessage implements IEvent { + public final String meetingId; + public final Long timestamp; + + public RecordChapterBreakMessage(String meetingId, Long timestamp) { + this.meetingId = meetingId; + this.timestamp = timestamp; + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java index d862ee9d1edc2f080e6c288363fcba71d2e24052..438c6f81f9ab158ac873fc7b020c42278344df3e 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java @@ -6,11 +6,13 @@ import org.bigbluebutton.app.screenshare.events.*; import com.google.gson.Gson; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; +import org.bigbluebutton.app.screenshare.MeetingManager; public class EventListenerImp implements IEventListener { private static Logger log = Red5LoggerFactory.getLogger(EventListenerImp.class, "screenshare"); private ConnectionInvokerService sender; - + private MeetingManager meetingManager; + @Override public void handleMessage(IEvent event) { if (event instanceof ScreenShareStartedEvent) { @@ -27,6 +29,9 @@ public class EventListenerImp implements IEventListener { sendIsScreenSharingResponse((IsScreenSharingResponse) event); } else if (event instanceof ScreenShareClientPing) { sendScreenShareClientPing((ScreenShareClientPing) event); + } else if (event instanceof RecordChapterBreakMessage) { + RecordChapterBreakMessage rcbm = (RecordChapterBreakMessage) event; + meetingManager.stopStartAllRecordings(rcbm.meetingId); } } @@ -202,7 +207,10 @@ public class EventListenerImp implements IEventListener { } - + + public void setMeetingManager(MeetingManager meetingManager) { + this.meetingManager = meetingManager; + } public void setMessageSender(ConnectionInvokerService sender) { this.sender = sender; diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java index ac7e46022893738bd814e29231c66dfb190c3844..394846d26fa8fe953a549f64d40bab365b738d00 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java @@ -34,9 +34,11 @@ import org.red5.server.api.stream.IServerStream; import org.red5.server.api.stream.IStreamListener; import org.red5.server.stream.ClientBroadcastStream; import org.slf4j.Logger; - +import org.red5.server.scheduling.QuartzSchedulingService; import com.google.gson.Gson; - +import org.bigbluebutton.app.screenshare.MeetingManager; +import org.bigbluebutton.app.screenshare.VideoStreamListener; +import org.bigbluebutton.app.screenshare.VideoStream; import org.bigbluebutton.app.screenshare.EventRecordingService; import org.bigbluebutton.app.screenshare.IScreenShareApplication; import org.bigbluebutton.app.screenshare.ScreenshareStreamListener; @@ -44,21 +46,27 @@ import org.bigbluebutton.app.screenshare.ScreenshareStreamListener; public class Red5AppAdapter extends MultiThreadedApplicationAdapter { private static Logger log = Red5LoggerFactory.getLogger(Red5AppAdapter.class, "screenshare"); - private EventRecordingService recordingService; - private final Map<String, IStreamListener> streamListeners = new HashMap<String, IStreamListener>(); + // Scheduler + private QuartzSchedulingService scheduler; + private EventRecordingService recordingService; private IScreenShareApplication app; private String streamBaseUrl; private ConnectionInvokerService sender; private String recordingDirectory; private final Pattern STREAM_ID_PATTERN = Pattern.compile("(.*)-(.*)-(.*)$"); - + + private MeetingManager meetingManager; + private int packetTimeout = 10000; + @Override public boolean appStart(IScope app) { super.appStart(app); log.info("BBB Screenshare appStart"); sender.setAppScope(app); + // get the scheduler + scheduler = (QuartzSchedulingService) getContext().getBean(QuartzSchedulingService.BEAN_NAME); return true; } @@ -148,12 +156,14 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { app.streamStarted(meetingId, streamId, url); boolean recordVideoStream = app.recordStream(meetingId, streamId); - if (recordVideoStream) { - recordStream(stream); - ScreenshareStreamListener listener = new ScreenshareStreamListener(recordingService, recordingDirectory); - stream.addStreamListener(listener); - streamListeners.put(conn.getScope().getName() + "-" + stream.getPublishedName(), listener); - } + VideoStreamListener listener = new VideoStreamListener(meetingId, streamId, + recordVideoStream, recordingDirectory, packetTimeout, scheduler, recordingService); + ClientBroadcastStream cstream = (ClientBroadcastStream) this.getBroadcastStream(conn.getScope(), stream.getPublishedName()); + stream.addStreamListener(listener); + VideoStream vstream = new VideoStream(stream, listener, cstream); + vstream.startRecording(); + + meetingManager.addStream(meetingId, vstream); Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); @@ -182,43 +192,12 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { String streamId = stream.getPublishedName(); Matcher matcher = STREAM_ID_PATTERN.matcher(stream.getPublishedName()); if (matcher.matches()) { - String meetingId = matcher.group(1).trim(); - app.streamStopped(meetingId, streamId); - - boolean recordVideoStream = app.recordStream(meetingId, streamId); - if (recordVideoStream) { - IConnection conn = Red5.getConnectionLocal(); - String scopeName; - if (conn != null) { - scopeName = conn.getScope().getName(); - } else { - log.info("Connection local was null, using scope name from the stream: {}", stream); - scopeName = stream.getScope().getName(); - } - IStreamListener listener = streamListeners.remove(scopeName + "-" + stream.getPublishedName()); - if (listener != null) { - stream.removeStreamListener(listener); - } - - String filename = recordingDirectory; - if (!filename.endsWith("/")) { - filename.concat("/"); - } - - filename = filename.concat(meetingId).concat("/").concat(stream.getPublishedName()).concat(".flv"); - - long publishDuration = (System.currentTimeMillis() - stream.getCreationTime()) / 1000; - - Map<String, String> event = new HashMap<String, String>(); - event.put("module", "Deskshare"); - event.put("timestamp", genTimestamp().toString()); - event.put("meetingId", scopeName); - event.put("stream", stream.getPublishedName()); - event.put("file", filename); - event.put("duration", new Long(publishDuration).toString()); - event.put("eventName", "DeskshareStoppedEvent"); - recordingService.record(scopeName, event); - } + String meetingId = matcher.group(1).trim(); + app.streamStopped(meetingId, streamId); + + boolean recordVideoStream = app.recordStream(meetingId, streamId); + meetingManager.streamBroadcastClose(meetingId, streamId); + Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); @@ -234,27 +213,10 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { } } - /** - * A hook to record a stream. A file is written in webapps/video/streams/ - * @param stream - */ - private void recordStream(IBroadcastStream stream) { - IConnection conn = Red5.getConnectionLocal(); - long now = System.currentTimeMillis(); - String recordingStreamName = stream.getPublishedName(); // + "-" + now; /** Comment out for now...forgot why I added this - ralam */ - - try { - log.info("Recording stream " + recordingStreamName ); - ClientBroadcastStream cstream = (ClientBroadcastStream) this.getBroadcastStream(conn.getScope(), stream.getPublishedName()); - cstream.saveAs(recordingStreamName, false); - } catch(Exception e) { - log.error("ERROR while recording stream " + e.getMessage()); - e.printStackTrace(); - } + public void setMeetingManager(MeetingManager meetingManager) { + this.meetingManager = meetingManager; } - - public void setEventRecordingService(EventRecordingService s) { recordingService = s; } diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala index 0e360cd810cf60dd08347d85ece9f94502339c94..7bc9e89f34ad0c3e18d7b5ee751e2fc4fee460cc 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala @@ -1,8 +1,8 @@ package org.bigbluebutton.app.screenshare.redis -import akka.actor.{Actor, ActorLogging, ActorRef, Props} import com.fasterxml.jackson.databind.JsonNode -import org.bigbluebutton.app.screenshare.server.sessions.messages.{MeetingCreated, MeetingEnded} +import org.bigbluebutton.app.screenshare.server.sessions.messages.{MeetingCreated, MeetingEnded, RecordingChapterBreak} +import akka.actor.{Actor, ActorLogging, ActorRef, Props} import scala.reflect.runtime.universe._ import org.bigbluebutton.common2.msgs._ @@ -56,6 +56,12 @@ class ReceivedJsonMsgHandlerActor(screenshareManager: ActorRef) } yield { screenshareManager ! new MeetingEnded(m.body.meetingId) } + case RecordingChapterBreakSysMsg.NAME => + for { + m <- deserialize[RecordingChapterBreakSysMsg](jsonNode) + } yield { + screenshareManager ! new RecordingChapterBreak(m.body.meetingId, m.body.timestamp) + } case _ => // log.error("Cannot route envelope name " + envelope.name) // do nothing diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala index 5fbe4f31a5e9d4fcec37ed03c89c5d31e7975eea..d3cfbb370fd9efe1a48157a944c36a528e88e0da 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala @@ -18,11 +18,11 @@ */ package org.bigbluebutton.app.screenshare.server.sessions -import akka.actor.{Actor, ActorLogging, ActorSystem, Props} import org.bigbluebutton.app.screenshare.StreamInfo import org.bigbluebutton.app.screenshare.server.sessions.Session.StopSession +import akka.actor.{Actor, ActorLogging, ActorSystem, Props} import scala.collection.mutable.HashMap -import org.bigbluebutton.app.screenshare.events.{IEventsMessageBus, IsScreenSharingResponse, ScreenShareRequestTokenFailedResponse} +import org.bigbluebutton.app.screenshare.events.{IEventsMessageBus, IsScreenSharingResponse, RecordChapterBreakMessage, ScreenShareRequestTokenFailedResponse} import org.bigbluebutton.app.screenshare.server.sessions.messages._ object ScreenshareManager { @@ -57,10 +57,15 @@ class ScreenshareManager(val aSystem: ActorSystem, val bus: IEventsMessageBus) case msg: MeetingEnded => handleMeetingHasEnded(msg) case msg: MeetingCreated => handleMeetingCreated(msg) case msg: ClientPongMessage => handleClientPongMessage(msg) + case msg: RecordingChapterBreak => handleRecordingChapterBreak(msg) case msg: Any => log.warning("Unknown message " + msg) } + private def handleRecordingChapterBreak(msg: RecordingChapterBreak): Unit = { + bus.send(new RecordChapterBreakMessage(msg.meetingId, msg.timestamp)) + } + private def handleClientPongMessage(msg: ClientPongMessage) { if (log.isDebugEnabled) { log.debug("Received ClientPongMessage message for meeting=[" + msg.meetingId + "]") diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala index 9819d807237dd969a5e531f3ee2dd47a9c227611..23cbcb2212b3b9e23982d789a4d8458bbdd8ee7e 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala @@ -47,4 +47,6 @@ case class MeetingEnded(meetingId: String) case class MeetingCreated(meetingId: String, record: Boolean) -case class ClientPongMessage(meetingId: String, userId: String, streamId: String, timestamp: Long) \ No newline at end of file +case class ClientPongMessage(meetingId: String, userId: String, streamId: String, timestamp: Long) + +case class RecordingChapterBreak(meetingId: String, timestamp: Long) \ No newline at end of file diff --git a/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml b/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml index 1773918626992aed94e15e4aa3e6ec59458e4e46..cab5891b7ea3e795dd5cb0cdb27310b8db5786f3 100755 --- a/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml +++ b/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml @@ -52,7 +52,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <property name="recordingDirectory" value="${recordingDirectory}"/> <property name="application" ref="screenShareApplication"/> <property name="messageSender" ref="connectionInvokerService"/> + <property name="meetingManager" ref="meetingManager"/> </bean> + + <bean id="meetingManager" class="org.bigbluebutton.app.screenshare.MeetingManager"/> <bean id="screenshare.service" class="org.bigbluebutton.app.screenshare.red5.Red5AppService"> <property name="appHandler" ref="red5AppHandler"/> @@ -77,6 +80,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <bean id="eventListenerImp" class="org.bigbluebutton.app.screenshare.red5.EventListenerImp"> <property name="messageSender" ref="connectionInvokerService"/> + <property name="meetingManager" ref="meetingManager"/> </bean> <bean id="jnlpConfigurator" class="org.bigbluebutton.app.screenshare.server.servlet.JnlpConfigurator"> diff --git a/bbb-video/src/main/java/org/bigbluebutton/app/video/ConnectionInvokerService.java b/bbb-video/src/main/java/org/bigbluebutton/app/video/ConnectionInvokerService.java new file mode 100755 index 0000000000000000000000000000000000000000..e1a68b223e0011df36fe86bec05c1756422c29fb --- /dev/null +++ b/bbb-video/src/main/java/org/bigbluebutton/app/video/ConnectionInvokerService.java @@ -0,0 +1,172 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. +* +*/ +package org.bigbluebutton.app.video; + +import com.google.gson.Gson; +import org.bigbluebutton.red5.pubsub.message.ClientMessage; +import org.bigbluebutton.red5.pubsub.message.ValidateConnTokenRespMsg; +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.api.IConnection; +import org.red5.server.api.scope.IScope; +import org.red5.server.api.so.ISharedObject; +import org.red5.server.api.so.ISharedObjectService; +import org.red5.server.so.SharedObjectService; +import org.red5.server.util.ScopeUtils; +import org.slf4j.Logger; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.*; + +public class ConnectionInvokerService { + private static Logger log = Red5LoggerFactory.getLogger(ConnectionInvokerService.class, "video"); + + private final String CONN = "RED5-"; + private static final int NTHREADS = 1; + private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); + private static final ExecutorService runExec = Executors.newFixedThreadPool(3); + + private BlockingQueue<ClientMessage> messages; + private volatile boolean sendMessages = false; + private IScope bbbAppScope; + + private final long SEND_TIMEOUT = 5000000000L; // 5s + + private Long lastMsgLengthLog = System.currentTimeMillis(); + + public ConnectionInvokerService() { + messages = new LinkedBlockingQueue<ClientMessage>(); + } + + public void setAppScope(IScope scope) { + bbbAppScope = scope; + } + + public void start() { + sendMessages = true; + Runnable sender = new Runnable() { + public void run() { + while (sendMessages) { + ClientMessage message; + try { + if (System.currentTimeMillis() - lastMsgLengthLog > 60000) { + lastMsgLengthLog = System.currentTimeMillis(); + log.info("Message queue length = " + messages.size()); + } + message = messages.take(); + if (log.isTraceEnabled()) { + log.trace("Took message from queue: " + message.getMessageName()); + } + sendMessageToClient(message); + if (log.isTraceEnabled()) { + log.trace("Sent message to client: " + message.getMessageName()); + } + } catch (Exception e) { + Marker sendingException = MarkerFactory.getMarker("SENDING_EXCEPTION"); + log.error(sendingException, "Exception while sending message to client.", e); + } + } + } + }; + exec.execute(sender); + } + + public void stop() { + sendMessages = false; + runExec.shutdown(); + } + + public void sendMessage(final ClientMessage message) { + if (log.isTraceEnabled()) { + log.trace("Queue message: " + message.getMessageName()); + } + messages.offer(message); + } + + private void sendMessageToClient(ClientMessage message) { + if (message instanceof ValidateConnTokenRespMsg) { + handleValidateConnTokenRespMsg((ValidateConnTokenRespMsg) message); + } + } + + private void handleValidateConnTokenRespMsg(ValidateConnTokenRespMsg msg) { + if (log.isTraceEnabled()) { + log.trace("Handle direct message: " + msg.getMessageName() + " conn=" + msg.connId); + } + + IScope meetingScope = getScope(msg.meetingId); + if (meetingScope != null) { + String userId = msg.userId; + IConnection conn = getConnection(meetingScope, userId); + if (conn != null) { + if (conn.isConnected() && !msg.authzed) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", msg.meetingId); + logData.put("userId", userId); + logData.put("authzed", msg.authzed); + logData.put("app", "video"); + logData.put("event", "close_unauthorized_connection"); + logData.put("description", "Closing unauthorized connection."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.info("Closing unauthorized connection: data={}", logStr); + conn.close(); + } + } + } + } + + private IConnection getConnectionWithConnId(IScope scope, String connId) { + for (IConnection conn : scope.getClientConnections()) { + String connID = (String) conn.getSessionId(); + if (connID != null && connID.equals(connId)) { + return conn; + } + } + + log.warn("Failed to get connection for connId = " + connId); + return null; + } + + private IConnection getConnection(IScope scope, String userId) { + for (IConnection conn : scope.getClientConnections()) { + String connID = (String) conn.getAttribute("USERID"); + if (connID != null && connID.equals(userId)) { + return conn; + } + } + + log.warn("Failed to get connection for userId = " + userId); + return null; + } + + public IScope getScope(String meetingID) { + if (bbbAppScope != null) { + return bbbAppScope.getContext().resolveScope("video"); + } else { + log.error("BigBlueButton Scope not initialized. No messages are going to the Flash client!"); + } + + return null; + } +} diff --git a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java index e5232265cd073705601637de19312fa91b78deb8..d66101b725803063a9b395063722824af370ad2e 100755 --- a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java +++ b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java @@ -65,11 +65,14 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { private final Map<String, VideoRotator> videoRotators = new HashMap<String, VideoRotator>(); private MeetingManager meetingManager; + private ConnectionInvokerService connInvokerService; @Override public boolean appStart(IScope app) { super.appStart(app); log.info("BBB Video appStart"); + connInvokerService.setAppScope(app); + // get the scheduler scheduler = (QuartzSchedulingService) getContext().getBean(QuartzSchedulingService.BEAN_NAME); return true; @@ -88,13 +91,18 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { params = new Object[2]; params[0] = "UNKNOWN-MEETING-ID"; params[1] = "UNKNOWN-USER-ID"; + params[2] = "UNKNOWN-AUTH-TOKEN"; } - String meetingId = ((String) params[0]).toString(); + String meetingId = connection.getScope().getName(); String userId = ((String) params[1]).toString(); + String authToken = ((String) params[2]).toString(); Red5.getConnectionLocal().setAttribute("MEETING_ID", meetingId); Red5.getConnectionLocal().setAttribute("USERID", userId); + Red5.getConnectionLocal().setAttribute("AUTH_TOKEN", authToken); + + publisher.validateConnAuthToken(meetingId, userId, authToken); String connType = getConnectionType(Red5.getConnectionLocal().getType()); String sessionId = Red5.getConnectionLocal().getSessionId(); @@ -455,4 +463,8 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { } } } + + public void setConnInvokerService(ConnectionInvokerService connInvokerService) { + this.connInvokerService = connInvokerService; + } } diff --git a/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MeetingMessageHandler.java b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MeetingMessageHandler.java index 48e7aa3d299c540124027d4d5dfd29c0d2e735a7..bbb12614ff59f0219fdedb61deace4d6ab04cdf5 100755 --- a/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MeetingMessageHandler.java +++ b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MeetingMessageHandler.java @@ -2,8 +2,10 @@ package org.bigbluebutton.red5.pubsub; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import org.bigbluebutton.app.video.ConnectionInvokerService; import org.bigbluebutton.app.video.MeetingManager; import org.bigbluebutton.red5.pubsub.message.RecordChapterBreakMessage; +import org.bigbluebutton.red5.pubsub.message.ValidateConnTokenRespMsg; public class MeetingMessageHandler implements MessageHandler { @@ -15,9 +17,15 @@ public class MeetingMessageHandler implements MessageHandler { private final String ENVELOPE = "envelope"; private final String CORE = "core"; + private final String USERID = "userId"; + private final String AUTHZED = "authzed"; + private final String CONN = "conn"; + private final String RecordingChapterBreakSysMsg = "RecordingChapterBreakSysMsg"; + private final String ValidateConnAuthTokenSysRespMsg = "ValidateConnAuthTokenSysRespMsg"; private MeetingManager meetingManager; + private ConnectionInvokerService connInvokerService; public void handleMessage(String pattern, String channel, String message) { JsonParser parser = new JsonParser(); @@ -41,10 +49,26 @@ public class MeetingMessageHandler implements MessageHandler { RecordChapterBreakMessage chBreak = new RecordChapterBreakMessage(meetingId, timestamp); meetingManager.stopStartAllRecordings(meetingId); } + } else if (ValidateConnAuthTokenSysRespMsg.equals(name)) { + if (body.has(MEETING_ID) && body.has(USERID) + && body.has(AUTHZED) && body.has(CONN)) { + String meetingId = body.get(MEETING_ID).getAsString(); + String userId = body.get(USERID).getAsString(); + Boolean authzed = body.get(AUTHZED).getAsBoolean(); + String conn = body.get(CONN).getAsString(); + if (conn.equals("VIDEO")) { + ValidateConnTokenRespMsg vctrm = new ValidateConnTokenRespMsg(meetingId, userId, authzed, conn); + connInvokerService.sendMessage(vctrm); + } + } } } public void setMeetingManager(MeetingManager mgr) { this.meetingManager = mgr; } + + public void setConnInvokerService(ConnectionInvokerService connInvokerService) { + this.connInvokerService = connInvokerService; + } } diff --git a/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java index 5b6de28e9828ec66f29d209317d1d7629d61d824..2ad2ecd9988ab4bc2ac0074ff39c4f53d27ef8b2 100755 --- a/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java +++ b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/MessagePublisher.java @@ -3,11 +3,10 @@ package org.bigbluebutton.red5.pubsub; import org.bigbluebutton.common.messages.MessagingConstants; import org.bigbluebutton.common.messages.UserSharedWebcamMessage; import org.bigbluebutton.common.messages.UserUnshareWebcamRequestMessage; - import java.util.HashMap; import java.util.Map; - import com.google.gson.Gson; +import org.bigbluebutton.common2.msgs.*; public class MessagePublisher { @@ -16,7 +15,40 @@ public class MessagePublisher { public void setMessageSender(MessageSender sender) { this.sender = sender; } - + + private Map<String, Object> buildEnvelope(String name, Map<String, String> routing) { + Map<String, Object> envelope = new HashMap<String, Object>(); + envelope.put("name", name); + envelope.put("routing", routing); + return envelope; + } + + private Map<String, String> buildRouting() { + Map<String, String> routing = new HashMap<String, String>(); + routing.put("msgType", "SYSTEM"); + routing.put("sender", "bbb-video"); + return routing; + } + + public void validateConnAuthToken(String meetingId, String userId, String authToken) { + BbbClientMsgHeader header = new BbbClientMsgHeader("ValidateConnAuthTokenSysMsg", meetingId, userId); + ValidateConnAuthTokenSysMsgBody body = new ValidateConnAuthTokenSysMsgBody(meetingId, + userId, authToken, "VIDEO"); + ValidateConnAuthTokenSysMsg msg = new ValidateConnAuthTokenSysMsg(header, body); + + Map<String, String> routing = buildRouting(); + Map<String, Object> envelope = buildEnvelope("ValidateConnAuthTokenSysMsg", routing); + + Map<String, Object> fullmsg = new HashMap<String, Object>(); + fullmsg.put("envelope", envelope); + fullmsg.put("core", msg); + + Gson gson = new Gson(); + String json = gson.toJson(fullmsg); + + sender.send("to-akka-apps-redis-channel", json); + } + // Polling public void userSharedWebcamMessage(String meetingId, String userId, String streamId) { UserSharedWebcamMessage msg = new UserSharedWebcamMessage(meetingId, userId, streamId); diff --git a/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/message/ClientMessage.java b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/message/ClientMessage.java new file mode 100755 index 0000000000000000000000000000000000000000..e96c2a079691bc8e58b1e2f024c3d96397ecd372 --- /dev/null +++ b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/message/ClientMessage.java @@ -0,0 +1,25 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. +* +*/ +package org.bigbluebutton.red5.pubsub.message; + + +public interface ClientMessage { + + String getMessageName(); +} diff --git a/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/message/ValidateConnTokenRespMsg.java b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/message/ValidateConnTokenRespMsg.java new file mode 100755 index 0000000000000000000000000000000000000000..10e2c8b09113d2ba158a7a2b6d69d8ad0dfac453 --- /dev/null +++ b/bbb-video/src/main/java/org/bigbluebutton/red5/pubsub/message/ValidateConnTokenRespMsg.java @@ -0,0 +1,20 @@ +package org.bigbluebutton.red5.pubsub.message; + +public class ValidateConnTokenRespMsg implements ClientMessage { + + public final String meetingId; + public final String connId; + public final String userId; + public final Boolean authzed; + + public ValidateConnTokenRespMsg(String meetingId, String userId, Boolean authzed, String connId) { + this.meetingId = meetingId; + this.connId = connId; + this.authzed = authzed; + this.userId = userId; + } + + public String getMessageName() { + return "ValidateConnTokenRespMsg"; + } +} diff --git a/bbb-video/src/main/webapp/WEB-INF/red5-web.xml b/bbb-video/src/main/webapp/WEB-INF/red5-web.xml index b5a9854bb8cf52f730797e13bb2f7c76e221d47e..99342ccabc69412ba0debecb8fa262a60287c2e2 100755 --- a/bbb-video/src/main/webapp/WEB-INF/red5-web.xml +++ b/bbb-video/src/main/webapp/WEB-INF/red5-web.xml @@ -50,8 +50,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <property name="eventRecordingService" ref="redisRecorder"/> <property name="messagePublisher" ref="redisPublisher"/> <property name="meetingManager" ref="meetingManager"/> + <property name="connInvokerService" ref="connInvokerService"/> </bean> + <bean id="connInvokerService" class="org.bigbluebutton.app.video.ConnectionInvokerService" + init-method="start" destroy-method="stop"/> + <bean id="meetingManager" class="org.bigbluebutton.app.video.MeetingManager"/> <bean id="redisPublisher" class="org.bigbluebutton.red5.pubsub.MessagePublisher"> @@ -82,6 +86,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <bean id="meetingMessageHandler" class="org.bigbluebutton.red5.pubsub.MeetingMessageHandler"> <property name="meetingManager" ref="meetingManager"/> + <property name="connInvokerService" ref="connInvokerService"/> </bean> <bean id="receivedMessageHandler" class="org.bigbluebutton.red5.pubsub.ReceivedMessageHandler" diff --git a/bigbluebutton-apps/build.gradle b/bigbluebutton-apps/build.gradle index 38de8618dc76055a13d95a805f1cced5831ecd2f..67871ae8ba5bf9c0b9970f5ad24ec8f734b454af 100755 --- a/bigbluebutton-apps/build.gradle +++ b/bigbluebutton-apps/build.gradle @@ -64,7 +64,7 @@ dependencies { providedCompile 'org.apache.commons:commons-lang3:3.6' compile 'org.bigbluebutton:bbb-common-message_2.12:0.0.19-SNAPSHOT' - compile 'org.bigbluebutton:bbb-apps-common_2.12:0.0.2' + compile 'org.bigbluebutton:bbb-apps-common_2.12:0.0.3-SNAPSHOT' } test { diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as index 501e118685ca4af6e7fb0e5077078ba6a0ed4527..f6b1b6877f49ef3c35626ef38ebcb2b6dea8600f 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as @@ -154,7 +154,8 @@ package org.bigbluebutton.main.model var curTime:Number = new Date().getTime(); // Create connection with the server. - nc.connect( this.baseURI, "portTestMeetingId-" + curTime, "portTestDummyUserId-" + curTime); + nc.connect( this.baseURI, "portTestMeetingId-" + curTime, + "portTestDummyUserId-" + curTime, "portTestDummyToken-" + curTime); status = "Connecting..."; } catch( e : ArgumentError ) { // Invalid parameters. diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as index ec1341728c34e5e6bacc8905f4958650cd80a057..58c7b4c8c17d63f14820ffab265adfa596bbe3a2 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as @@ -29,14 +29,13 @@ package org.bigbluebutton.modules.videoconf.business import flash.media.H264VideoStreamSettings; import flash.net.NetConnection; import flash.net.NetStream; - import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.getClassLogger; import org.bigbluebutton.core.BBB; import org.bigbluebutton.core.Options; import org.bigbluebutton.core.UsersUtil; import org.bigbluebutton.core.managers.ReconnectionManager; - import org.bigbluebutton.main.api.JSLog; + import org.bigbluebutton.core.model.LiveMeeting; import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.modules.videoconf.events.ConnectedEvent; import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; @@ -82,7 +81,8 @@ package org.bigbluebutton.modules.videoconf.business } public function connect():void { - nc.connect(_url, UsersUtil.getInternalMeetingID(), UsersUtil.getMyUserID()); + nc.connect(_url, UsersUtil.getInternalMeetingID(), + UsersUtil.getMyUserID(), LiveMeeting.inst().me.authToken); } private function onAsyncError(event:AsyncErrorEvent):void{