mirror of https://github.com/jitsi/jitsi-meet
parent
ab5c2e9ded
commit
e6f6884c36
@ -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. |
||||
*/ |
||||
export const API_ID |
||||
= typeof getConfigParamsFromUrl === 'function' |
||||
? getConfigParamsFromUrl().jitsi_meet_external_api_id |
||||
: undefined; |
||||
= parseURLParams(window.location).jitsi_meet_external_api_id; |
||||
|
@ -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; |
@ -1,72 +1,247 @@ |
||||
/* @flow */ |
||||
|
||||
declare var config: Object; |
||||
import JSSHA from 'jssha'; |
||||
|
||||
import parseURLParams from './parseURLParams'; |
||||
|
||||
declare var $: Object; |
||||
|
||||
/** |
||||
* The config keys to ignore because, for example, their values identify scripts |
||||
* and it is not desireable to inject these through URL params. |
||||
* |
||||
* @private |
||||
* @type Array |
||||
*/ |
||||
const _KEYS_TO_IGNORE = [ |
||||
'analyticsScriptUrls', |
||||
'callStatsCustomScriptUrl' |
||||
]; |
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename); |
||||
|
||||
// XXX The functions getRoomName and parseURLParams are split out of
|
||||
// functions.js because they are bundled in both app.bundle and
|
||||
// do_external_connect, webpack 1 does not support tree shaking, and we don't
|
||||
// want all functions to be bundled in do_external_connect.
|
||||
export { default as getRoomName } from './getRoomName'; |
||||
export { parseURLParams }; |
||||
|
||||
/* eslint-disable no-shadow */ |
||||
|
||||
/** |
||||
* Builds and returns the room name. |
||||
* 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}. |
||||
* |
||||
* @returns {string} |
||||
* @param {Object} config - The configuration object. |
||||
* @param {string} roomName - The name of the room/conference. |
||||
* @returns {void} |
||||
*/ |
||||
export 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; |
||||
export function chooseBOSHAddress(config: Object, roomName: string) { |
||||
if (!roomName) { |
||||
return; |
||||
} |
||||
|
||||
const { boshList } = config; |
||||
|
||||
if (!boshList || !Array.isArray(boshList) || !boshList.length) { |
||||
return; |
||||
} |
||||
|
||||
return roomName; |
||||
// 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 */ |
||||
|
||||
/** |
||||
* 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} endpoint - The name of HTTP endpoint to which to send |
||||
* the HTTP POST request. |
||||
* @param {string} roomName - The name of the conference room for which config |
||||
* is requested. |
||||
* @param {Function} complete - The callback to invoke upon success or failure. |
||||
* @returns {void} |
||||
*/ |
||||
export function obtainConfig( |
||||
endpoint: string, |
||||
roomName: string, |
||||
complete: Function) { |
||||
logger.info(`Send config request to ${endpoint} for room: ${roomName}`); |
||||
$.ajax( |
||||
endpoint, |
||||
{ |
||||
contentType: 'application/json', |
||||
data: JSON.stringify({ roomName }), |
||||
dataType: 'json', |
||||
method: 'POST', |
||||
|
||||
error(jqXHR, textStatus, errorThrown) { |
||||
logger.error('Get config error: ', jqXHR, errorThrown); |
||||
complete(false, `Get config response status: ${textStatus}`); |
||||
}, |
||||
success(data) { |
||||
const { config, interfaceConfig, loggingConfig } = window; |
||||
|
||||
try { |
||||
overrideConfigJSON( |
||||
config, interfaceConfig, loggingConfig, |
||||
data); |
||||
complete(true); |
||||
} catch (e) { |
||||
logger.error('Parse config error: ', e); |
||||
complete(false, e); |
||||
} |
||||
} |
||||
} |
||||
); |
||||
} |
||||
|
||||
/* eslint-disable max-params, no-shadow */ |
||||
|
||||
/** |
||||
* Parses the parameters from the URL and returns them as a JS object. |
||||
* Overrides JSON properties in {@code config} and |
||||
* {@code interfaceConfig} Objects with the values from {@code newConfig}. |
||||
* |
||||
* @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} |
||||
* @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 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('='); |
||||
let value; |
||||
|
||||
try { |
||||
value = param[1]; |
||||
if (!dontParse) { |
||||
value |
||||
= JSON.parse( |
||||
decodeURIComponent(param[1]).replace(/\\&/, '&')); |
||||
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; |
||||
} |
||||
} catch (e) { |
||||
const msg = `Failed to parse URL parameter value: ${String(value)}`; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* 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); |
||||
|
||||
console.warn(msg, e); |
||||
window.onerror && window.onerror(msg, null, null, null, e); |
||||
const { config, interfaceConfig, loggingConfig } = window; |
||||
const json = {}; |
||||
|
||||
return; |
||||
// 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]; |
||||
} |
||||
} |
||||
} |
||||
params[param[0]] = value; |
||||
}); |
||||
} |
||||
|
||||
return params; |
||||
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; |
||||
} |
Loading…
Reference in new issue