[RN] Support JSON Web Token (JWT)
Make 'Add jwt module to react' work on mobile.
This commit is contained in:
parent
ab5c2e9ded
commit
e6f6884c36
|
@ -1,9 +1,7 @@
|
||||||
/* global config, createConnectionExternally */
|
/* global config, createConnectionExternally */
|
||||||
|
|
||||||
import {
|
import getRoomName from '../react/features/base/config/getRoomName';
|
||||||
getRoomName,
|
import parseURLParams from '../react/features/base/config/parseURLParams';
|
||||||
parseURLParams
|
|
||||||
} from '../react/features/base/config/functions';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements external connect using createConnectionExternally function defined
|
* Implements external connect using createConnectionExternally function defined
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
declare var getConfigParamsFromUrl: Function;
|
// XXX The function parseURLParams is exported by the feature base/config (as
|
||||||
|
// defined in the terminology of react/). However, this file is (very likely)
|
||||||
|
// bundled in external_api in addition to app.bundle and, consequently, it is
|
||||||
|
// best to import as little as possible here (rather than the whole feature
|
||||||
|
// base/config) in order to minimize the amount of source code bundled into
|
||||||
|
// multiple bundles.
|
||||||
|
import parseURLParams from '../../react/features/base/config/parseURLParams';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JitsiMeetExternalAPI id - unique for a webpage.
|
* JitsiMeetExternalAPI id - unique for a webpage.
|
||||||
*/
|
*/
|
||||||
export const API_ID
|
export const API_ID
|
||||||
= typeof getConfigParamsFromUrl === 'function'
|
= parseURLParams(window.location).jitsi_meet_external_api_id;
|
||||||
? getConfigParamsFromUrl().jitsi_meet_external_api_id
|
|
||||||
: undefined;
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
||||||
|
|
||||||
var JSSHA = require('jssha');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
/**
|
|
||||||
* Looks for a list of possible BOSH addresses in 'config.boshList' and
|
|
||||||
* sets the value of 'config.bosh' based on that list and 'roomName'.
|
|
||||||
* @param config the configuration object.
|
|
||||||
* @param roomName the name of the room/conference.
|
|
||||||
*/
|
|
||||||
chooseAddress: function(config, roomName) {
|
|
||||||
if (!roomName || !config.boshList || !Array.isArray(config.boshList) ||
|
|
||||||
!config.boshList.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This implements the actual choice of an entry in the list based on
|
|
||||||
// roomName. Please consider the implications for existing deployments
|
|
||||||
// before introducing changes.
|
|
||||||
var hash = (new JSSHA(roomName, 'TEXT')).getHash('SHA-1', 'HEX');
|
|
||||||
var n = parseInt("0x"+hash.substr(-6));
|
|
||||||
var idx = n % config.boshList.length;
|
|
||||||
var attemptFirstAddress;
|
|
||||||
|
|
||||||
config.bosh = config.boshList[idx];
|
|
||||||
logger.log('Setting config.bosh to ' + config.bosh +
|
|
||||||
' (idx=' + idx + ')');
|
|
||||||
|
|
||||||
if (config.boshAttemptFirstList &&
|
|
||||||
Array.isArray(config.boshAttemptFirstList) &&
|
|
||||||
config.boshAttemptFirstList.length > 0) {
|
|
||||||
|
|
||||||
idx = n % config.boshAttemptFirstList.length;
|
|
||||||
attemptFirstAddress = config.boshAttemptFirstList[idx];
|
|
||||||
|
|
||||||
if (attemptFirstAddress != config.bosh) {
|
|
||||||
config.boshAttemptFirst = attemptFirstAddress;
|
|
||||||
logger.log('Setting config.boshAttemptFirst=' +
|
|
||||||
attemptFirstAddress + ' (idx=' + idx + ')');
|
|
||||||
} else {
|
|
||||||
logger.log('Not setting boshAttemptFirst, address matches.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,52 +0,0 @@
|
||||||
/* global $, config, interfaceConfig */
|
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
||||||
|
|
||||||
var configUtil = require('./Util');
|
|
||||||
|
|
||||||
var HttpConfig = {
|
|
||||||
/**
|
|
||||||
* Sends HTTP POST request to specified <tt>endpoint</tt>. In request
|
|
||||||
* the name of the room is included in JSON format:
|
|
||||||
* {
|
|
||||||
* "rooomName": "someroom12345"
|
|
||||||
* }
|
|
||||||
* @param endpoint the name of HTTP endpoint to which HTTP POST request will
|
|
||||||
* be sent.
|
|
||||||
* @param roomName the name of the conference room for which config will be
|
|
||||||
* requested.
|
|
||||||
* @param complete
|
|
||||||
*/
|
|
||||||
obtainConfig: function (endpoint, roomName, complete) {
|
|
||||||
logger.info(
|
|
||||||
"Send config request to " + endpoint + " for room: " + roomName);
|
|
||||||
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
endpoint,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify({"roomName": roomName}),
|
|
||||||
dataType: 'json',
|
|
||||||
error: function(jqXHR, textStatus, errorThrown) {
|
|
||||||
logger.error("Get config error: ", jqXHR, errorThrown);
|
|
||||||
var error = "Get config response status: " + textStatus;
|
|
||||||
complete(false, error);
|
|
||||||
},
|
|
||||||
success: function(data) {
|
|
||||||
try {
|
|
||||||
configUtil.overrideConfigJSON(
|
|
||||||
config, interfaceConfig, data);
|
|
||||||
complete(true);
|
|
||||||
return;
|
|
||||||
} catch (exception) {
|
|
||||||
logger.error("Parse config error: ", exception);
|
|
||||||
complete(false, exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = HttpConfig;
|
|
|
@ -1,93 +0,0 @@
|
||||||
/* global config, interfaceConfig, loggingConfig */
|
|
||||||
|
|
||||||
import { parseURLParams } from '../../react/features/base/config';
|
|
||||||
|
|
||||||
import configUtils from './Util';
|
|
||||||
|
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL params with this prefix should be merged to config.
|
|
||||||
*/
|
|
||||||
const CONFIG_PREFIX = 'config.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL params with this prefix should be merged to interface config.
|
|
||||||
*/
|
|
||||||
const INTERFACE_CONFIG_PREFIX = 'interfaceConfig.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Config keys to be ignored.
|
|
||||||
*
|
|
||||||
* @type Set
|
|
||||||
*/
|
|
||||||
const KEYS_TO_IGNORE = new Set([
|
|
||||||
'analyticsScriptUrls',
|
|
||||||
'callStatsCustomScriptUrl'
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL params with this prefix should be merged to logging config.
|
|
||||||
*/
|
|
||||||
const LOGGING_CONFIG_PREFIX = 'loggingConfig.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert 'URL_PARAMS' to JSON object
|
|
||||||
* We have:
|
|
||||||
* {
|
|
||||||
* "config.disableAudioLevels": false,
|
|
||||||
* "config.channelLastN": -1,
|
|
||||||
* "interfaceConfig.APP_NAME": "Jitsi Meet"
|
|
||||||
* }
|
|
||||||
* We want to have:
|
|
||||||
* {
|
|
||||||
* "config": {
|
|
||||||
* "disableAudioLevels": false,
|
|
||||||
* "channelLastN": -1
|
|
||||||
* },
|
|
||||||
* interfaceConfig: {
|
|
||||||
* "APP_NAME": "Jitsi Meet"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
export function setConfigParametersFromUrl() {
|
|
||||||
// Parsing config params from URL hash.
|
|
||||||
const params = parseURLParams(window.location);
|
|
||||||
|
|
||||||
const configJSON = {
|
|
||||||
config: {},
|
|
||||||
interfaceConfig: {},
|
|
||||||
loggingConfig: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key in params) {
|
|
||||||
if (typeof key === 'string') {
|
|
||||||
let confObj = null;
|
|
||||||
let confKey;
|
|
||||||
|
|
||||||
if (key.indexOf(CONFIG_PREFIX) === 0) {
|
|
||||||
confObj = configJSON.config;
|
|
||||||
confKey = key.substr(CONFIG_PREFIX.length);
|
|
||||||
|
|
||||||
} else if (key.indexOf(INTERFACE_CONFIG_PREFIX) === 0) {
|
|
||||||
confObj = configJSON.interfaceConfig;
|
|
||||||
confKey
|
|
||||||
= key.substr(INTERFACE_CONFIG_PREFIX.length);
|
|
||||||
} else if (key.indexOf(LOGGING_CONFIG_PREFIX) === 0) {
|
|
||||||
confObj = configJSON.loggingConfig;
|
|
||||||
confKey = key.substr(LOGGING_CONFIG_PREFIX.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent passing some parameters which can inject scripts
|
|
||||||
if (confObj && !KEYS_TO_IGNORE.has(confKey)) {
|
|
||||||
confObj[confKey] = params[key];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warn('Invalid config key: ', key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configUtils.overrideConfigJSON(
|
|
||||||
config, interfaceConfig, loggingConfig,
|
|
||||||
configJSON);
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
||||||
|
|
||||||
var ConfigUtil = {
|
|
||||||
/**
|
|
||||||
* Method overrides JSON properties in <tt>config</tt> and
|
|
||||||
* <tt>interfaceConfig</tt> Objects with the values from <tt>newConfig</tt>
|
|
||||||
* @param config the config object for which we'll be overriding properties
|
|
||||||
* @param interfaceConfig the interfaceConfig object for which we'll be
|
|
||||||
* overriding properties.
|
|
||||||
* @param loggingConfig the logging config object for which we'll be
|
|
||||||
* overriding properties.
|
|
||||||
* @param newConfig object containing configuration properties. Destination
|
|
||||||
* object is selected based on root property name:
|
|
||||||
* {
|
|
||||||
* config: {
|
|
||||||
* // config.js properties to be
|
|
||||||
* },
|
|
||||||
* interfaceConfig: {
|
|
||||||
* // interface_config.js properties here
|
|
||||||
* },
|
|
||||||
* loggingConfig: {
|
|
||||||
* // logging_config.js properties here
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
overrideConfigJSON: function (config,
|
|
||||||
interfaceConfig, loggingConfig, newConfig) {
|
|
||||||
var configRoot, key, value, confObj;
|
|
||||||
for (configRoot in newConfig) {
|
|
||||||
confObj = null;
|
|
||||||
if (configRoot == "config") {
|
|
||||||
confObj = config;
|
|
||||||
} else if (configRoot == "interfaceConfig") {
|
|
||||||
confObj = interfaceConfig;
|
|
||||||
} else if (configRoot == "loggingConfig") {
|
|
||||||
confObj = loggingConfig;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (key in newConfig[configRoot]) {
|
|
||||||
value = newConfig[configRoot][key];
|
|
||||||
if (confObj[key] && typeof confObj[key] !== typeof value) {
|
|
||||||
logger.log("Overriding a " + configRoot +
|
|
||||||
" property with a property of different type.");
|
|
||||||
}
|
|
||||||
logger.info("Overriding " + key + " with: " + value);
|
|
||||||
confObj[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = ConfigUtil;
|
|
|
@ -18,7 +18,8 @@ import {
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
export function appInit() {
|
export function appInit() {
|
||||||
return (dispatch, getState) => init(getState());
|
return (dispatch: Dispatch<*>, getState: Function) =>
|
||||||
|
init(getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +32,7 @@ export function appInit() {
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
export function appNavigate(uri) {
|
export function appNavigate(uri) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const oldDomain = getDomain(state);
|
const oldDomain = getDomain(state);
|
||||||
const defaultURL = state['features/app'].app._getDefaultURL();
|
const defaultURL = state['features/app'].app._getDefaultURL();
|
||||||
|
@ -43,14 +44,12 @@ export function appNavigate(uri) {
|
||||||
// If the specified URI does not identify a domain, use the app's
|
// If the specified URI does not identify a domain, use the app's
|
||||||
// default.
|
// default.
|
||||||
if (typeof domain === 'undefined') {
|
if (typeof domain === 'undefined') {
|
||||||
domain
|
domain = _parseURIString(defaultURL).domain;
|
||||||
= _parseURIString(defaultURL)
|
|
||||||
.domain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (room) {
|
if (room) {
|
||||||
const splitUrl = uri.split(domain);
|
const splitURL = uri.split(domain);
|
||||||
const urlWithoutDomain = splitUrl[splitUrl.length - 1];
|
const urlWithoutDomain = splitURL[splitURL.length - 1];
|
||||||
|
|
||||||
urlObject = new URL(urlWithoutDomain, `https://${domain}`);
|
urlObject = new URL(urlWithoutDomain, `https://${domain}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,247 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
declare var config: Object;
|
import JSSHA from 'jssha';
|
||||||
|
|
||||||
|
import parseURLParams from './parseURLParams';
|
||||||
|
|
||||||
|
declare var $: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and returns the room name.
|
* The config keys to ignore because, for example, their values identify scripts
|
||||||
|
* and it is not desireable to inject these through URL params.
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @private
|
||||||
|
* @type Array
|
||||||
*/
|
*/
|
||||||
export function getRoomName(): ?string {
|
const _KEYS_TO_IGNORE = [
|
||||||
const { getroomnode } = config;
|
'analyticsScriptUrls',
|
||||||
const path = window.location.pathname;
|
'callStatsCustomScriptUrl'
|
||||||
let roomName;
|
];
|
||||||
|
|
||||||
// Determine the room node from the URL.
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
if (getroomnode && typeof getroomnode === 'function') {
|
|
||||||
roomName = getroomnode.call(config, path);
|
// XXX The functions getRoomName and parseURLParams are split out of
|
||||||
} else {
|
// functions.js because they are bundled in both app.bundle and
|
||||||
// Fall back to the default strategy of making assumptions about how the
|
// do_external_connect, webpack 1 does not support tree shaking, and we don't
|
||||||
// URL maps to the room (name). It currently assumes a deployment in
|
// want all functions to be bundled in do_external_connect.
|
||||||
// which the last non-directory component of the path (name) is the
|
export { default as getRoomName } from './getRoomName';
|
||||||
// room.
|
export { parseURLParams };
|
||||||
roomName
|
|
||||||
= path.substring(path.lastIndexOf('/') + 1).toLowerCase()
|
/* eslint-disable no-shadow */
|
||||||
|| undefined;
|
|
||||||
|
/**
|
||||||
|
* Looks for a list of possible BOSH addresses in {@code config.boshList} and
|
||||||
|
* sets the value of {@code config.bosh} based on that list and
|
||||||
|
* {@code roomName}.
|
||||||
|
*
|
||||||
|
* @param {Object} config - The configuration object.
|
||||||
|
* @param {string} roomName - The name of the room/conference.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function chooseBOSHAddress(config: Object, roomName: string) {
|
||||||
|
if (!roomName) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomName;
|
const { boshList } = config;
|
||||||
|
|
||||||
|
if (!boshList || !Array.isArray(boshList) || !boshList.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implements the actual choice of an entry in the list based on
|
||||||
|
// roomName. Please consider the implications for existing deployments
|
||||||
|
// before introducing changes.
|
||||||
|
const hash = (new JSSHA(roomName, 'TEXT')).getHash('SHA-1', 'HEX');
|
||||||
|
const n = parseInt(hash.substr(-6), 16);
|
||||||
|
let idx = n % boshList.length;
|
||||||
|
|
||||||
|
config.bosh = boshList[idx];
|
||||||
|
logger.log(`Setting config.bosh to ${config.bosh} (idx=${idx})`);
|
||||||
|
|
||||||
|
const { boshAttemptFirstList } = config;
|
||||||
|
|
||||||
|
if (boshAttemptFirstList
|
||||||
|
&& Array.isArray(boshAttemptFirstList)
|
||||||
|
&& boshAttemptFirstList.length > 0) {
|
||||||
|
idx = n % boshAttemptFirstList.length;
|
||||||
|
|
||||||
|
const attemptFirstAddress = boshAttemptFirstList[idx];
|
||||||
|
|
||||||
|
if (attemptFirstAddress === config.bosh) {
|
||||||
|
logger.log('Not setting config.boshAttemptFirst, address matches.');
|
||||||
|
} else {
|
||||||
|
config.boshAttemptFirst = attemptFirstAddress;
|
||||||
|
logger.log(
|
||||||
|
`Setting config.boshAttemptFirst=${attemptFirstAddress} (idx=${
|
||||||
|
idx})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-enable no-shadow */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the parameters from the URL and returns them as a JS object.
|
* Sends HTTP POST request to specified <tt>endpoint</tt>. In request the name
|
||||||
|
* of the room is included in JSON format:
|
||||||
|
* {
|
||||||
|
* "rooomName": "someroom12345"
|
||||||
|
* }.
|
||||||
*
|
*
|
||||||
* @param {string} url - URL to parse.
|
* @param {string} endpoint - The name of HTTP endpoint to which to send
|
||||||
* @param {boolean} dontParse - If false or undefined some transformations
|
* the HTTP POST request.
|
||||||
* (for parsing the value as JSON) are going to be executed.
|
* @param {string} roomName - The name of the conference room for which config
|
||||||
* @param {string} source - Values - "hash"/"search" if "search" the parameters
|
* is requested.
|
||||||
* will parsed from location.search otherwise from location.hash.
|
* @param {Function} complete - The callback to invoke upon success or failure.
|
||||||
* @returns {Object}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export function parseURLParams(
|
export function obtainConfig(
|
||||||
url: URL,
|
endpoint: string,
|
||||||
dontParse: boolean = false,
|
roomName: string,
|
||||||
source: string = 'hash'): Object {
|
complete: Function) {
|
||||||
const paramStr = source === 'search' ? url.search : url.hash;
|
logger.info(`Send config request to ${endpoint} for room: ${roomName}`);
|
||||||
const params = {};
|
$.ajax(
|
||||||
|
endpoint,
|
||||||
|
{
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({ roomName }),
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'POST',
|
||||||
|
|
||||||
// eslint-disable-next-line newline-per-chained-call
|
error(jqXHR, textStatus, errorThrown) {
|
||||||
paramStr && paramStr.substr(1).split('&').forEach(part => {
|
logger.error('Get config error: ', jqXHR, errorThrown);
|
||||||
const param = part.split('=');
|
complete(false, `Get config response status: ${textStatus}`);
|
||||||
let value;
|
},
|
||||||
|
success(data) {
|
||||||
|
const { config, interfaceConfig, loggingConfig } = window;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
value = param[1];
|
overrideConfigJSON(
|
||||||
if (!dontParse) {
|
config, interfaceConfig, loggingConfig,
|
||||||
value
|
data);
|
||||||
= JSON.parse(
|
complete(true);
|
||||||
decodeURIComponent(param[1]).replace(/\\&/, '&'));
|
} catch (e) {
|
||||||
|
logger.error('Parse config error: ', e);
|
||||||
|
complete(false, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
const msg = `Failed to parse URL parameter value: ${String(value)}`;
|
|
||||||
|
|
||||||
console.warn(msg, e);
|
|
||||||
window.onerror && window.onerror(msg, null, null, null, e);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
params[param[0]] = value;
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
return params;
|
/* eslint-disable max-params, no-shadow */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides JSON properties in {@code config} and
|
||||||
|
* {@code interfaceConfig} Objects with the values from {@code newConfig}.
|
||||||
|
*
|
||||||
|
* @param {Object} config - The config Object in which we'll be overriding
|
||||||
|
* properties.
|
||||||
|
* @param {Object} interfaceConfig - The interfaceConfig Object in which we'll
|
||||||
|
* be overriding properties.
|
||||||
|
* @param {Object} loggingConfig - The loggingConfig Object in which we'll be
|
||||||
|
* overriding properties.
|
||||||
|
* @param {Object} json - Object containing configuration properties.
|
||||||
|
* Destination object is selected based on root property name:
|
||||||
|
* {
|
||||||
|
* config: {
|
||||||
|
* // config.js properties here
|
||||||
|
* },
|
||||||
|
* interfaceConfig: {
|
||||||
|
* // interface_config.js properties here
|
||||||
|
* },
|
||||||
|
* loggingConfig: {
|
||||||
|
* // logging_config.js properties here
|
||||||
|
* }
|
||||||
|
* }.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function overrideConfigJSON(
|
||||||
|
config: Object, interfaceConfig: Object, loggingConfig: Object,
|
||||||
|
json: Object) {
|
||||||
|
for (const configName of Object.keys(json)) {
|
||||||
|
let configObj;
|
||||||
|
|
||||||
|
if (configName === 'config') {
|
||||||
|
configObj = config;
|
||||||
|
} else if (configName === 'interfaceConfig') {
|
||||||
|
configObj = interfaceConfig;
|
||||||
|
} else if (configName === 'loggingConfig') {
|
||||||
|
configObj = loggingConfig;
|
||||||
|
}
|
||||||
|
if (configObj) {
|
||||||
|
const configJSON = json[configName];
|
||||||
|
|
||||||
|
for (const key of Object.keys(configJSON)) {
|
||||||
|
const oldValue = configObj[key];
|
||||||
|
const newValue = configJSON[key];
|
||||||
|
|
||||||
|
if (oldValue && typeof oldValue !== typeof newValue) {
|
||||||
|
logger.log(
|
||||||
|
`Overriding a ${configName
|
||||||
|
} property with a property of different type.`);
|
||||||
|
}
|
||||||
|
logger.info(`Overriding ${key} with: ${newValue}`);
|
||||||
|
configObj[key] = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-enable max-params, no-shadow */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts 'URL_PARAMS' to JSON object.
|
||||||
|
* We have:
|
||||||
|
* {
|
||||||
|
* "config.disableAudioLevels": false,
|
||||||
|
* "config.channelLastN": -1,
|
||||||
|
* "interfaceConfig.APP_NAME": "Jitsi Meet"
|
||||||
|
* }.
|
||||||
|
* We want to have:
|
||||||
|
* {
|
||||||
|
* "config": {
|
||||||
|
* "disableAudioLevels": false,
|
||||||
|
* "channelLastN": -1
|
||||||
|
* },
|
||||||
|
* interfaceConfig: {
|
||||||
|
* "APP_NAME": "Jitsi Meet"
|
||||||
|
* }
|
||||||
|
* }.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function setConfigFromURLParams() {
|
||||||
|
const params = parseURLParams(window.location);
|
||||||
|
|
||||||
|
const { config, interfaceConfig, loggingConfig } = window;
|
||||||
|
const json = {};
|
||||||
|
|
||||||
|
// TODO We're still in the middle ground between old Web with config,
|
||||||
|
// interfaceConfig, and loggingConfig used via global variables and new Web
|
||||||
|
// and mobile reading the respective values from the redux store. On React
|
||||||
|
// Native there's no interfaceConfig at all yet and loggingConfig is not
|
||||||
|
// loaded but there's a default value in the redux store.
|
||||||
|
config && (json.config = {});
|
||||||
|
interfaceConfig && (json.interfaceConfig = {});
|
||||||
|
loggingConfig && (json.loggingConfig = {});
|
||||||
|
|
||||||
|
for (const param of Object.keys(params)) {
|
||||||
|
const objEnd = param.indexOf('.');
|
||||||
|
|
||||||
|
if (objEnd !== -1) {
|
||||||
|
const obj = param.substring(0, objEnd);
|
||||||
|
|
||||||
|
if (json.hasOwnProperty(obj)) {
|
||||||
|
const key = param.substring(objEnd + 1);
|
||||||
|
|
||||||
|
// Prevent passing some parameters which can inject scripts.
|
||||||
|
if (key && _KEYS_TO_IGNORE.indexOf(key) === -1) {
|
||||||
|
json[obj][key] = params[param];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideConfigJSON(config, interfaceConfig, loggingConfig, json);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
declare var config: Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and returns the room name.
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export default function getRoomName(): ?string {
|
||||||
|
const { getroomnode } = config;
|
||||||
|
const path = window.location.pathname;
|
||||||
|
let roomName;
|
||||||
|
|
||||||
|
// Determine the room node from the URL.
|
||||||
|
if (getroomnode && typeof getroomnode === 'function') {
|
||||||
|
roomName = getroomnode.call(config, path);
|
||||||
|
} else {
|
||||||
|
// Fall back to the default strategy of making assumptions about how the
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return roomName;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the parameters from the URL and returns them as a JS object.
|
||||||
|
*
|
||||||
|
* @param {string} url - URL to parse.
|
||||||
|
* @param {boolean} dontParse - If false or undefined some transformations
|
||||||
|
* (for parsing the value as JSON) are going to be executed.
|
||||||
|
* @param {string} source - Values - "hash"/"search" if "search" the parameters
|
||||||
|
* will parsed from location.search otherwise from location.hash.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export default function parseURLParams(
|
||||||
|
url: URL,
|
||||||
|
dontParse: boolean = false,
|
||||||
|
source: string = 'hash'): Object {
|
||||||
|
const paramStr = source === 'search' ? url.search : url.hash;
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line newline-per-chained-call
|
||||||
|
paramStr && paramStr.substr(1).split('&').forEach(part => {
|
||||||
|
const param = part.split('=');
|
||||||
|
const key = param[0];
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
value = param[1];
|
||||||
|
if (!dontParse) {
|
||||||
|
value
|
||||||
|
= JSON.parse(decodeURIComponent(value).replace(/\\&/, '&'));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
const msg = `Failed to parse URL parameter value: ${String(value)}`;
|
||||||
|
|
||||||
|
console.warn(msg, e);
|
||||||
|
window.onerror && window.onerror(msg, null, null, null, e);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
|
@ -21,11 +21,12 @@ export function connect() {
|
||||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { options } = state['features/base/connection'];
|
const { options } = state['features/base/connection'];
|
||||||
|
const { issuer, jwt } = state['features/jwt'];
|
||||||
const { room } = state['features/base/conference'];
|
const { room } = state['features/base/conference'];
|
||||||
const connection
|
const connection
|
||||||
= new JitsiMeetJS.JitsiConnection(
|
= new JitsiMeetJS.JitsiConnection(
|
||||||
options.appId,
|
options.appId,
|
||||||
options.token,
|
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
bosh:
|
bosh:
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { loadScript } from '../../base/util';
|
/* @flow */
|
||||||
|
|
||||||
import {
|
import { setConfigFromURLParams } from '../../base/config';
|
||||||
setConfigParametersFromUrl
|
import { loadScript } from '../../base/util';
|
||||||
} from '../../../../modules/config/URLProcessor';
|
|
||||||
|
|
||||||
import JitsiMeetJS from './_';
|
import JitsiMeetJS from './_';
|
||||||
|
|
||||||
|
@ -10,6 +9,28 @@ declare var APP: Object;
|
||||||
|
|
||||||
const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
|
const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JitsiLocalTrack model from the given device id.
|
||||||
|
*
|
||||||
|
* @param {string} type - The media type of track being created. Expected values
|
||||||
|
* are "video" or "audio".
|
||||||
|
* @param {string} deviceId - The id of the target media source.
|
||||||
|
* @returns {Promise<JitsiLocalTrack>}
|
||||||
|
*/
|
||||||
|
export function createLocalTrack(type: string, deviceId: string) {
|
||||||
|
return (
|
||||||
|
JitsiMeetJS.createLocalTracks({
|
||||||
|
cameraDeviceId: deviceId,
|
||||||
|
devices: [ type ],
|
||||||
|
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
firefox_fake_device:
|
||||||
|
window.config && window.config.firefox_fake_device,
|
||||||
|
micDeviceId: deviceId
|
||||||
|
})
|
||||||
|
.then(([ jitsiLocalTrack ]) => jitsiLocalTrack));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether a specific JitsiConnectionErrors instance indicates a
|
* Determines whether a specific JitsiConnectionErrors instance indicates a
|
||||||
* fatal JitsiConnection error.
|
* fatal JitsiConnection error.
|
||||||
|
@ -38,56 +59,43 @@ export function isFatalJitsiConnectionError(error: string) {
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
export function loadConfig(host: string, path: string = '/config.js') {
|
export function loadConfig(host: string, path: string = '/config.js') {
|
||||||
// Returns config.js file from global scope. We can't use the version that's
|
let promise;
|
||||||
// being used for the React Native app because the old/current Web app uses
|
|
||||||
// config from the global scope.
|
|
||||||
if (typeof APP !== 'undefined') {
|
|
||||||
// FIXME The following call to setConfigParametersFromUrl is bad design
|
|
||||||
// but URLProcessor still deals with the global variables config,
|
|
||||||
// interfaceConfig, and loggingConfig and loadConfig. As the latter will
|
|
||||||
// surely change in the future, so will the former then.
|
|
||||||
setConfigParametersFromUrl();
|
|
||||||
|
|
||||||
return Promise.resolve(window.config);
|
if (typeof APP === 'undefined') {
|
||||||
|
promise
|
||||||
|
= loadScript(new URL(path, host).toString())
|
||||||
|
.then(() => {
|
||||||
|
const { config } = window;
|
||||||
|
|
||||||
|
// We don't want to pollute global scope.
|
||||||
|
window.config = undefined;
|
||||||
|
|
||||||
|
if (typeof config !== 'object') {
|
||||||
|
throw new Error('window.config is not an object');
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(`Failed to load ${path} from ${host}`, err);
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Return config.js file from global scope. We can't use the version
|
||||||
|
// that's being used for the React Native app because the old/current
|
||||||
|
// Web app uses config from the global scope.
|
||||||
|
promise = Promise.resolve(window.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadScript(new URL(path, host).toString())
|
// FIXME It's neither here nor there at the time of this writing where
|
||||||
.then(() => {
|
// config, interfaceConfig, and loggingConfig should be overwritten by URL
|
||||||
const config = window.config;
|
// params.
|
||||||
|
promise = promise.then(value => {
|
||||||
|
setConfigFromURLParams();
|
||||||
|
|
||||||
// We don't want to pollute global scope.
|
return value;
|
||||||
window.config = undefined;
|
});
|
||||||
|
|
||||||
if (typeof config !== 'object') {
|
return promise;
|
||||||
throw new Error('window.config is not an object');
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(`Failed to load ${path} from ${host}`, err);
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a JitsiLocalTrack model from the given device id.
|
|
||||||
*
|
|
||||||
* @param {string} type - The media type of track being created. Expected values
|
|
||||||
* are "video" or "audio".
|
|
||||||
* @param {string} deviceId - The id of the target media source.
|
|
||||||
* @returns {Promise<JitsiLocalTrack>}
|
|
||||||
*/
|
|
||||||
export function createLocalTrack(type, deviceId) {
|
|
||||||
return JitsiMeetJS.createLocalTracks({
|
|
||||||
cameraDeviceId: deviceId,
|
|
||||||
devices: [ type ],
|
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
firefox_fake_device:
|
|
||||||
window.config && window.config.firefox_fake_device,
|
|
||||||
micDeviceId: deviceId
|
|
||||||
})
|
|
||||||
.then(([ jitsiLocalTrack ]) => jitsiLocalTrack);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/* global APP, config */
|
/* global APP, config */
|
||||||
|
|
||||||
import BoshAddressChoice from '../../../modules/config/BoshAddressChoice';
|
|
||||||
import HttpConfigFetch from '../../../modules/config/HttpConfigFetch';
|
|
||||||
import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
|
import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
|
||||||
|
|
||||||
|
import { chooseBOSHAddress, obtainConfig } from '../base/config';
|
||||||
import { RouteRegistry } from '../base/react';
|
import { RouteRegistry } from '../base/react';
|
||||||
|
|
||||||
import { Conference } from './components';
|
import { Conference } from './components';
|
||||||
|
@ -47,15 +46,11 @@ function _initConference() {
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function _obtainConfig(location, room) {
|
function _obtainConfig(location, room) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) =>
|
||||||
HttpConfigFetch.obtainConfig(location, room, (success, error) => {
|
obtainConfig(location, room, (success, error) => {
|
||||||
if (success) {
|
success ? resolve() : reject(error);
|
||||||
resolve();
|
})
|
||||||
} else {
|
);
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,7 +82,7 @@ function _obtainConfigAndInit() {
|
||||||
null, 'dialog.connectError', err);
|
null, 'dialog.connectError', err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
BoshAddressChoice.chooseAddress(config, room);
|
chooseBOSHAddress(config, room);
|
||||||
_initConference();
|
_initConference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue