diff --git a/bigbluebutton-html5/private/static/guest-wait/guest-wait.html b/bigbluebutton-html5/private/static/guest-wait/guest-wait.html index e2181188dbd3e66fe4e4901175bc28668dd5ff8b..91d89a410d9769164e37f7d2fa4cfb26476c69fd 100755 --- a/bigbluebutton-html5/private/static/guest-wait/guest-wait.html +++ b/bigbluebutton-html5/private/static/guest-wait/guest-wait.html @@ -61,11 +61,13 @@ </style> <script type="text/javascript"> + const REDIRECT_TIMEOUT = 15000; + function updateMessage(message) { document.querySelector('#content > p').innerHTML = message; } - var lobbyMessage = ''; + let lobbyMessage = ''; function updateLobbyMessage(message) { if (message !== lobbyMessage) { lobbyMessage = message; @@ -91,25 +93,42 @@ const urlTest = new URL(`${window.location.origin}${GUEST_WAIT_ENDPOINT}`); const concatedParams = sessionToken.concat('&redirect=false'); urlTest.search = concatedParams; - return fetch(urlTest, {method: 'get'}); + return fetch(urlTest, { method: 'get' }); + }; + + function redirect(message, url) { + disableAnimation(); + updateMessage(message); + setTimeout(() => { + window.location = url; + }, REDIRECT_TIMEOUT); }; function pollGuestStatus(token, attempt, limit, everyMs) { - setTimeout(function() { - var REDIRECT_STATUSES = ['ALLOW', 'DENY']; + setTimeout(function() { if (attempt >= limit) { disableAnimation(); - updateMessage('No response from Moderator'); + updateMessage('No response from a moderator.'); return; } fetchGuestWait(token) .then(async (resp) => await resp.json()) .then((data) => { - var status = data.response.guestStatus; + const code = data.response.returncode; + + if (code === 'FAILED') { + return redirect(data.response.message, data.response.url); + } + + const status = data.response.guestStatus; + + if (status === 'DENY') { + return redirect(data.response.message, data.response.url); + } - if (REDIRECT_STATUSES.includes(status)) { + if (status === 'ALLOW') { disableAnimation(); window.location = data.response.url; return; @@ -133,14 +152,14 @@ window.onload = function() { enableAnimation(); try { - var ATTEMPT_EVERY_MS = 5000; - var ATTEMPT_LIMIT = 100; + const ATTEMPT_EVERY_MS = 5000; + const ATTEMPT_LIMIT = 100; - var sessionToken = findSessionToken(); + const sessionToken = findSessionToken(); - if(!sessionToken) { + if (!sessionToken) { disableAnimation() - updateMessage('No session Token received'); + updateMessage('No session token received.'); return; } @@ -148,7 +167,7 @@ } catch (e) { disableAnimation(); console.error(e); - updateMessage('Error: more details in the console'); + updateMessage('Error: more details in the console.'); } }; </script> diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index 77049971a325d2109d268c2ad0ce4cb289056fae..3a010a4cf437163031b3c0416c857cf8404be54d 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -1283,141 +1283,112 @@ class ApiController { } ApiErrors errors = new ApiErrors() - boolean reject = false; - String sessionToken = sanitizeSessionToken(params.sessionToken) + String msgKey = "defaultKey" + String msgValue = "defaultValue" + String destURL = paramsProcessorUtil.getDefaultLogoutUrl() - UserSession us = getUserSession(sessionToken); - Meeting meeting = null; + // Do we have a sessionToken? If none, complain. + String sessionToken = sanitizeSessionToken(params.sessionToken) + if (sessionToken == null) { + msgKey = "missingToken" + msgValue = "Guest missing session token." + respondWithJSONError(msgKey, msgValue, destURL) + return + } + UserSession us = getUserSession(sessionToken) if (us == null) { - log.debug("No user with session token.") - reject = true; - } else { - meeting = meetingService.getMeeting(us.meetingID); - if (meeting == null || meeting.isForciblyEnded()) { - log.debug("Meeting not found.") - reject = true - } + msgKey = "missingSession" + msgValue = "Guest missing session." + respondWithJSONError(msgKey, msgValue, destURL) + return } - // Determine the logout url so we can send the user there. - String logoutUrl = us != null ? us.logoutUrl : paramsProcessorUtil.getDefaultLogoutUrl() - - if (reject) { - response.addHeader("Cache-Control", "no-cache") - withFormat { - json { - def builder = new JsonBuilder() - builder.response { - returncode RESP_CODE_FAILED - message "Could not process waiting guest." - logoutURL logoutUrl - } - render(contentType: "application/json", text: builder.toPrettyString()) - } - } - } else { - //check if exists the param redirect - boolean redirectClient = true; - - // Get the client url we stored in the join api call before - // being told to wait. - String clientURL = us.clientUrl; - String lobbyMsg = meeting.getGuestLobbyMessage() - log.info("clientURL = " + clientURL) - log.info("redirect = ." + redirectClient) - if (!StringUtils.isEmpty(params.redirect)) { - try { - redirectClient = Boolean.parseBoolean(params.redirect); - log.info("redirect 2 = ." + redirectClient) - } catch (Exception e) { - redirectClient = true; - } - } - - // The client url is ovewriten. Let's allow it. - if (!StringUtils.isEmpty(params.clientURL)) { - clientURL = params.clientURL; - } + Meeting meeting = meetingService.getMeeting(us.meetingID) + if (meeting == null) { + msgKey = "missingMeeting" + msgValue = "Meeting does not exist." + respondWithJSONError(msgKey, msgValue, destURL) + return + } - String guestWaitStatus = us.guestStatus + // Is this user joining a meeting that has been ended. If so, complain. + if (meeting.isForciblyEnded()) { + msgKey = "meetingEnded" + msgValue = "Meeting ended." + respondWithJSONError(msgKey, msgValue, destURL) + return + } - log.debug("GuestWaitStatus = " + guestWaitStatus) + String status = us.guestStatus + destURL = us.clientUrl + String lobbyMsg = meeting.getGuestLobbyMessage() - String msgKey = "guestAllowed" - String msgValue = "Guest allowed to join meeting." + Boolean redirectClient = true + if (!StringUtils.isEmpty(params.redirect)) { + try { + redirectClient = Boolean.parseBoolean(params.redirect) + } catch (Exception e) { + redirectClient = true + } + } - String destUrl = clientURL - log.debug("destUrl = " + destUrl) + String guestURL = paramsProcessorUtil.getDefaultGuestWaitURL() + "?sessionToken=" + sessionToken - if (guestWaitStatus.equals(GuestPolicy.WAIT)) { - meetingService.guestIsWaiting(us.meetingID, us.internalUserId); - clientURL = paramsProcessorUtil.getDefaultGuestWaitURL(); - destUrl = clientURL + "?sessionToken=" + sessionToken - log.debug("GuestPolicy.WAIT - destUrl = " + destUrl) + switch (status) { + case GuestPolicy.WAIT: + meetingService.guestIsWaiting(us.meetingID, us.internalUserId) + destURL = guestURL msgKey = "guestWait" - msgValue = "Guest waiting for approval to join meeting." + msgValue = "Please wait for a moderator to approve you joining the meeting." + // We force the response to not do a redirect. Otherwise, // the client would just be redirecting into this endpoint. redirectClient = false + break + case GuestPolicy.DENY: + destURL = meeting.getLogoutUrl() + msgKey = "guestDeny" + msgValue = "Guest denied of joining the meeting." + redirectClient = false + break + case GuestPolicy.ALLOW: + // IF the user was allowed to join but there is no room available in + // the meeting we must hold his approval + if (hasReachedMaxParticipants(meeting, us)) { + meetingService.guestIsWaiting(us.meetingID, us.internalUserId) + destURL = guestURL + msgKey = "seatWait" + msgValue = "Guest waiting for a seat in the meeting." + redirectClient = false + status = GuestPolicy.WAIT + } + break + default: + break + } - Map<String, Object> logData = new HashMap<String, Object>(); - logData.put("meetingid", us.meetingID); - logData.put("extMeetingid", us.externMeetingID); - logData.put("name", us.fullname); - logData.put("userid", us.internalUserId); - logData.put("sessionToken", sessionToken); - logData.put("logCode", "guest_wait"); - logData.put("description", "Guest waiting for approval."); - - Gson gson = new Gson(); - String logStr = gson.toJson(logData); - - log.info(" --analytics-- data=" + logStr); - - } else if (guestWaitStatus.equals(GuestPolicy.DENY)) { - destUrl = meeting.getLogoutUrl() - msgKey = "guestDenied" - msgValue = "Guest denied to join meeting." - - Map<String, Object> logData = new HashMap<String, Object>(); - logData.put("meetingid", us.meetingID); - logData.put("extMeetingid", us.externMeetingID); - logData.put("name", us.fullname); - logData.put("userid", us.internalUserId); - logData.put("sessionToken", sessionToken); - logData.put("logCode", "guest_denied"); - logData.put("description", "Guest denied."); - - Gson gson = new Gson(); - String logStr = gson.toJson(logData); - - log.info(" --analytics-- data=" + logStr); - } - - if (redirectClient) { - log.info("Redirecting to ${destUrl}"); - redirect(url: destUrl); - } else { - log.info("Successfully joined. Sending XML response."); - response.addHeader("Cache-Control", "no-cache") - withFormat { - json { - def builder = new JsonBuilder() - builder.response { - returncode RESP_CODE_SUCCESS - messageKey msgKey - message msgValue - meeting_id us.meetingID - user_id us.internalUserId - auth_token us.authToken - session_token session[sessionToken] - guestStatus guestWaitStatus - lobbyMessage lobbyMsg - url destUrl - } - render(contentType: "application/json", text: builder.toPrettyString()) + if (redirectClient) { + // User may join the meeting + redirect(url: destURL) + } else { + response.addHeader("Cache-Control", "no-cache") + withFormat { + json { + def builder = new JsonBuilder() + builder.response { + returncode RESP_CODE_SUCCESS + messageKey msgKey + message msgValue + meeting_id us.meetingID + user_id us.internalUserId + auth_token us.authToken + session_token session[sessionToken] + guestStatus status + lobbyMessage lobbyMsg + url destURL } + render(contentType: "application/json", text: builder.toPrettyString()) } } } @@ -2290,6 +2261,22 @@ class ApiController { return false; } + private void respondWithJSONError(msgKey, msgValue, destUrl) { + response.addHeader("Cache-Control", "no-cache") + withFormat { + json { + def builder = new JsonBuilder() + builder.response { + returncode RESP_CODE_FAILED + messageKey msgKey + message msgValue + url destUrl + } + render(contentType: "application/json", text: builder.toPrettyString()) + } + } + } + private void respondWithErrors(errorList, redirectResponse = false) { log.debug CONTROLLER_NAME + "#invalid" if (redirectResponse) {