diff --git a/react/features/app/actions.js b/react/features/app/actions.js index aca96b65faf7..dfac710ba786 100644 --- a/react/features/app/actions.js +++ b/react/features/app/actions.js @@ -15,6 +15,7 @@ import { connect, disconnect, setLocationURL } from '../base/connection'; import { loadConfig } from '../base/lib-jitsi-meet'; import { createDesiredLocalTracks } from '../base/tracks'; import { + getBackendSafeRoomName, getLocationContextRoot, parseURIString, toURLString @@ -85,7 +86,7 @@ export function appNavigate(uri: ?string) { let url = `${baseURL}config.js`; // XXX In order to support multiple shards, tell the room to the deployment. - room && (url += `?room=${room.toLowerCase()}`); + room && (url += `?room=${getBackendSafeRoomName(room)}`); let config; diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index fa6d79dfcf2f..81c506a5480f 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -23,7 +23,10 @@ import { participantUpdated } from '../participants'; import { getLocalTracks, trackAdded, trackRemoved } from '../tracks'; -import { getJitsiMeetGlobalNS } from '../util'; +import { + getBackendSafeRoomName, + getJitsiMeetGlobalNS +} from '../util'; import { AUTH_STATUS_CHANGED, @@ -388,8 +391,7 @@ export function createConference() { const conference = connection.initJitsiConference( - // XXX Lib-jitsi-meet does not accept uppercase letters. - room.toLowerCase(), { + getBackendSafeRoomName(room), { ...state['features/base/config'], applicationName: getName(), getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats, diff --git a/react/features/base/config/getRoomName.js b/react/features/base/config/getRoomName.js index d89a928bd1a1..87254fd908c3 100644 --- a/react/features/base/config/getRoomName.js +++ b/react/features/base/config/getRoomName.js @@ -1,5 +1,7 @@ /* @flow */ +import { getBackendSafeRoomName } from '../util'; + declare var config: Object; /** @@ -20,10 +22,8 @@ export default function getRoomName(): ?string { // URL maps to the room (name). It currently assumes a deployment in // which the last non-directory component of the path (name) is the // room. - roomName - = path.substring(path.lastIndexOf('/') + 1).toLowerCase() - || undefined; + roomName = path.substring(path.lastIndexOf('/') + 1) || undefined; } - return roomName; + return getBackendSafeRoomName(roomName); } diff --git a/react/features/base/connection/actions.native.js b/react/features/base/connection/actions.native.js index 7cbeca4caead..5ff33859e49f 100644 --- a/react/features/base/connection/actions.native.js +++ b/react/features/base/connection/actions.native.js @@ -9,7 +9,10 @@ import { getCurrentConference } from '../conference'; import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet'; -import { parseURIString } from '../util'; +import { + getBackendSafeRoomName, + parseURIString +} from '../util'; import { CONNECTION_DISCONNECTED, @@ -307,10 +310,7 @@ function _constructOptions(state) { // Append room to the URL's search. const { room } = state['features/base/conference']; - // XXX The Jitsi Meet deployments require the room argument to be in - // lower case at the time of this writing but, unfortunately, they do - // not ignore case themselves. - room && (bosh += `?room=${room.toLowerCase()}`); + room && (bosh += `?room=${getBackendSafeRoomName(room)}`); options.bosh = bosh; } diff --git a/react/features/base/connection/actions.web.js b/react/features/base/connection/actions.web.js index fe3ddb697db4..811cc5c067a2 100644 --- a/react/features/base/connection/actions.web.js +++ b/react/features/base/connection/actions.web.js @@ -6,6 +6,7 @@ declare var APP: Object; declare var config: Object; import { configureInitialDevices } from '../devices'; +import { getBackendSafeRoomName } from '../util'; export { connectionEstablished, @@ -21,8 +22,7 @@ import logger from './logger'; */ export function connect() { return (dispatch: Dispatch, getState: Function) => { - // XXX Lib-jitsi-meet does not accept uppercase letters. - const room = getState()['features/base/conference'].room.toLowerCase(); + const room = getBackendSafeRoomName(getState()['features/base/conference'].room); // XXX For web based version we use conference initialization logic // from the old app (at the moment of writing). diff --git a/react/features/base/util/uri.js b/react/features/base/util/uri.js index bb292d8e22b7..197b480e7609 100644 --- a/react/features/base/util/uri.js +++ b/react/features/base/util/uri.js @@ -96,6 +96,47 @@ function _fixURIStringScheme(uri: string) { return uri; } +/** + * Converts a room name to a backend-safe format. Properly lowercased and url encoded. + * + * @param {string?} room - The room name to convert. + * @returns {string?} + */ +export function getBackendSafeRoomName(room: ?string): ?string { + if (!room) { + return room; + } + + /* eslint-disable no-param-reassign */ + try { + // We do not know if we get an already encoded string at this point + // as different platforms do it differently, but we need a decoded one + // for sure. However since decoding a non-encoded string is a noop, we're safe + // doing it here. + room = decodeURIComponent(room); + } catch (e) { + // This can happen though if we get an unencoded string and it contains + // some characters that look like an encoded entity, but it's not. + // But in this case we're fine goin on... + } + + // Normalize the character set + room = room.normalize('NFKC'); + + // Only decoded and normalized strings can be lowercased properly. + room = room.toLowerCase(); + + // But we still need to (re)encode it. + room = encodeURIComponent(room); + /* eslint-enable no-param-reassign */ + + // Unfortunately we still need to lowercase it, because encoding a string will + // add some uppercase characters, but some backend services + // expect it to be full lowercase. However lowercasing an encoded string + // doesn't change the string value. + return room.toLowerCase(); +} + /** * Gets the (Web application) context root defined by a specific location (URI). * diff --git a/react/features/welcome/components/AbstractWelcomePage.js b/react/features/welcome/components/AbstractWelcomePage.js index 2c47289e9f30..5b741b895bab 100644 --- a/react/features/welcome/components/AbstractWelcomePage.js +++ b/react/features/welcome/components/AbstractWelcomePage.js @@ -192,7 +192,7 @@ export class AbstractWelcomePage extends Component { const onAppNavigateSettled = () => this._mounted && this.setState({ joining: false }); - this.props.dispatch(appNavigate(encodeURI(room))) + this.props.dispatch(appNavigate(room)) .then(onAppNavigateSettled, onAppNavigateSettled); } }