You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

functions.js 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /* @flow */
  2. import _ from 'lodash';
  3. import { _CONFIG_STORE_PREFIX } from './constants';
  4. import parseURLParams from './parseURLParams';
  5. declare var $: Object;
  6. /**
  7. * The config keys to whitelist, the keys that can be overridden.
  8. * Currently we can only whitelist the first part of the properties, like
  9. * 'p2p.useStunTurn' and 'p2p.enabled' we whitelist all p2p options.
  10. * The whitelist is used only for config.js.
  11. *
  12. * @private
  13. * @type Array
  14. */
  15. const WHITELISTED_KEYS = [
  16. '_peerConnStatusOutOfLastNTimeout',
  17. '_peerConnStatusRtcMuteTimeout',
  18. 'abTesting',
  19. 'alwaysVisibleToolbar',
  20. 'autoRecord',
  21. 'autoRecordToken',
  22. 'avgRtpStatsN',
  23. 'callFlowsEnabled',
  24. 'callStatsConfIDNamespace',
  25. 'callStatsID',
  26. 'callStatsSecret',
  27. 'callUUID',
  28. 'channelLastN',
  29. 'constraints',
  30. 'debug',
  31. 'debugAudioLevels',
  32. 'defaultLanguage',
  33. 'desktopSharingChromeDisabled',
  34. 'desktopSharingChromeExtId',
  35. 'desktopSharingChromeMinExtVersion',
  36. 'desktopSharingChromeSources',
  37. 'desktopSharingFrameRate',
  38. 'desktopSharingFirefoxDisabled',
  39. 'desktopSharingSources',
  40. 'disable1On1Mode',
  41. 'disableAEC',
  42. 'disableAGC',
  43. 'disableAP',
  44. 'disableAudioLevels',
  45. 'disableDesktopSharing',
  46. 'disableDesktopSharing',
  47. 'disableH264',
  48. 'disableHPF',
  49. 'disableNS',
  50. 'disableRemoteControl',
  51. 'disableRtx',
  52. 'disableSuspendVideo',
  53. 'displayJids',
  54. 'enableDisplayNameInStats',
  55. 'enableLipSync',
  56. 'enableLocalVideoFlip',
  57. 'enableRecording',
  58. 'enableRemb',
  59. 'enableStatsID',
  60. 'enableTalkWhileMuted',
  61. 'enableTcc',
  62. 'enableUserRolesBasedOnToken',
  63. 'etherpad_base',
  64. 'failICE',
  65. 'firefox_fake_device',
  66. 'forceJVB121Ratio',
  67. 'gatherStats',
  68. 'googleApiApplicationClientID',
  69. 'hiddenDomain',
  70. 'hosts',
  71. 'iAmRecorder',
  72. 'iAmSipGateway',
  73. 'iceTransportPolicy',
  74. 'ignoreStartMuted',
  75. 'minParticipants',
  76. 'nick',
  77. 'openBridgeChannel',
  78. 'p2p',
  79. 'preferH264',
  80. 'recordingType',
  81. 'requireDisplayName',
  82. 'resolution',
  83. 'startAudioMuted',
  84. 'startAudioOnly',
  85. 'startBitrate',
  86. 'startScreenSharing',
  87. 'startVideoMuted',
  88. 'startWithAudioMuted',
  89. 'startWithVideoMuted',
  90. 'testing',
  91. 'useIPv6',
  92. 'useNicks',
  93. 'useStunTurn',
  94. 'webrtcIceTcpDisable',
  95. 'webrtcIceUdpDisable'
  96. ];
  97. const logger = require('jitsi-meet-logger').getLogger(__filename);
  98. // XXX The functions getRoomName and parseURLParams are split out of
  99. // functions.js because they are bundled in both app.bundle and
  100. // do_external_connect, webpack 1 does not support tree shaking, and we don't
  101. // want all functions to be bundled in do_external_connect.
  102. export { default as getRoomName } from './getRoomName';
  103. export { parseURLParams };
  104. /**
  105. * Sends HTTP POST request to specified {@code endpoint}. In request the name
  106. * of the room is included in JSON format:
  107. * {
  108. * "rooomName": "someroom12345"
  109. * }.
  110. *
  111. * @param {string} endpoint - The name of HTTP endpoint to which to send
  112. * the HTTP POST request.
  113. * @param {string} roomName - The name of the conference room for which config
  114. * is requested.
  115. * @param {Function} complete - The callback to invoke upon success or failure.
  116. * @returns {void}
  117. */
  118. export function obtainConfig(
  119. endpoint: string,
  120. roomName: string,
  121. complete: Function) {
  122. logger.info(`Send config request to ${endpoint} for room: ${roomName}`);
  123. $.ajax(
  124. endpoint,
  125. {
  126. contentType: 'application/json',
  127. data: JSON.stringify({ roomName }),
  128. dataType: 'json',
  129. method: 'POST',
  130. error(jqXHR, textStatus, errorThrown) {
  131. logger.error('Get config error: ', jqXHR, errorThrown);
  132. complete(false, `Get config response status: ${textStatus}`);
  133. },
  134. success(data) {
  135. const { config, interfaceConfig, loggingConfig } = window;
  136. try {
  137. overrideConfigJSON(
  138. config, interfaceConfig, loggingConfig,
  139. data);
  140. complete(true);
  141. } catch (e) {
  142. logger.error('Parse config error: ', e);
  143. complete(false, e);
  144. }
  145. }
  146. }
  147. );
  148. }
  149. /* eslint-disable max-params, no-shadow */
  150. /**
  151. * Overrides JSON properties in {@code config} and
  152. * {@code interfaceConfig} Objects with the values from {@code newConfig}.
  153. * Overrides only the whitelisted keys.
  154. *
  155. * @param {Object} config - The config Object in which we'll be overriding
  156. * properties.
  157. * @param {Object} interfaceConfig - The interfaceConfig Object in which we'll
  158. * be overriding properties.
  159. * @param {Object} loggingConfig - The loggingConfig Object in which we'll be
  160. * overriding properties.
  161. * @param {Object} json - Object containing configuration properties.
  162. * Destination object is selected based on root property name:
  163. * {
  164. * config: {
  165. * // config.js properties here
  166. * },
  167. * interfaceConfig: {
  168. * // interface_config.js properties here
  169. * },
  170. * loggingConfig: {
  171. * // logging_config.js properties here
  172. * }
  173. * }.
  174. * @returns {void}
  175. */
  176. export function overrideConfigJSON(
  177. config: ?Object, interfaceConfig: ?Object, loggingConfig: ?Object,
  178. json: Object) {
  179. for (const configName of Object.keys(json)) {
  180. let configObj;
  181. if (configName === 'config') {
  182. configObj = config;
  183. } else if (configName === 'interfaceConfig') {
  184. configObj = interfaceConfig;
  185. } else if (configName === 'loggingConfig') {
  186. configObj = loggingConfig;
  187. }
  188. if (configObj) {
  189. const configJSON
  190. = _getWhitelistedJSON(configName, json[configName]);
  191. if (!_.isEmpty(configJSON)) {
  192. logger.info(
  193. `Extending ${configName} with: ${
  194. JSON.stringify(configJSON)}`);
  195. // eslint-disable-next-line arrow-body-style
  196. _.mergeWith(configObj, configJSON, (oldValue, newValue) => {
  197. // XXX We don't want to merge the arrays, we want to
  198. // overwrite them.
  199. return Array.isArray(oldValue) ? newValue : undefined;
  200. });
  201. }
  202. }
  203. }
  204. }
  205. /* eslint-enable max-params, no-shadow */
  206. /**
  207. * Whitelist only config.js, skips this for others configs
  208. * (interfaceConfig, loggingConfig).
  209. * Only extracts overridden values for keys we allow to be overridden.
  210. *
  211. * @param {string} configName - The config name, one of config,
  212. * interfaceConfig, loggingConfig.
  213. * @param {Object} configJSON - The object with keys and values to override.
  214. * @private
  215. * @returns {Object} - The result object only with the keys
  216. * that are whitelisted.
  217. */
  218. function _getWhitelistedJSON(configName, configJSON) {
  219. if (configName !== 'config') {
  220. return configJSON;
  221. }
  222. return _.pick(configJSON, WHITELISTED_KEYS);
  223. }
  224. /**
  225. * Restores a Jitsi Meet config.js from {@code localStorage} if it was
  226. * previously downloaded from a specific {@code baseURL} and stored with
  227. * {@link storeConfig}.
  228. *
  229. * @param {string} baseURL - The base URL from which the config.js was
  230. * previously downloaded and stored with {@code storeConfig}.
  231. * @returns {?Object} The Jitsi Meet config.js which was previously downloaded
  232. * from {@code baseURL} and stored with {@code storeConfig} if it was restored;
  233. * otherwise, {@code undefined}.
  234. */
  235. export function restoreConfig(baseURL: string): ?Object {
  236. let storage;
  237. const key = `${_CONFIG_STORE_PREFIX}/${baseURL}`;
  238. try {
  239. // XXX Even reading the property localStorage of window may throw an
  240. // error (which is user agent-specific behavior).
  241. storage = window.localStorage;
  242. const config = storage.getItem(key);
  243. if (config) {
  244. return JSON.parse(config) || undefined;
  245. }
  246. } catch (e) {
  247. // Somehow incorrect data ended up in the storage. Clean it up.
  248. storage && storage.removeItem(key);
  249. }
  250. return undefined;
  251. }
  252. /* eslint-disable max-params */
  253. /**
  254. * Inspects the hash part of the location URI and overrides values specified
  255. * there in the corresponding config objects given as the arguments. The syntax
  256. * is: {@code https://server.com/room#config.debug=true
  257. * &interfaceConfig.showButton=false&loggingConfig.something=1}.
  258. *
  259. * In the hash part each parameter will be parsed to JSON and then the root
  260. * object will be matched with the corresponding config object given as the
  261. * argument to this function.
  262. *
  263. * @param {Object} config - This is the general config.
  264. * @param {Object} interfaceConfig - This is the interface config.
  265. * @param {Object} loggingConfig - The logging config.
  266. * @param {URI} location - The new location to which the app is navigating to.
  267. * @returns {void}
  268. */
  269. export function setConfigFromURLParams(
  270. config: ?Object,
  271. interfaceConfig: ?Object,
  272. loggingConfig: ?Object,
  273. location: Object) {
  274. const params = parseURLParams(location);
  275. const json = {};
  276. // At this point we have:
  277. // params = {
  278. // "config.disableAudioLevels": false,
  279. // "config.channelLastN": -1,
  280. // "interfaceConfig.APP_NAME": "Jitsi Meet"
  281. // }
  282. // We want to have:
  283. // json = {
  284. // config: {
  285. // "disableAudioLevels": false,
  286. // "channelLastN": -1
  287. // },
  288. // interfaceConfig: {
  289. // "APP_NAME": "Jitsi Meet"
  290. // }
  291. // }
  292. config && (json.config = {});
  293. interfaceConfig && (json.interfaceConfig = {});
  294. loggingConfig && (json.loggingConfig = {});
  295. for (const param of Object.keys(params)) {
  296. let base = json;
  297. const names = param.split('.');
  298. const last = names.pop();
  299. for (const name of names) {
  300. base = base[name] = base[name] || {};
  301. }
  302. base[last] = params[param];
  303. }
  304. overrideConfigJSON(config, interfaceConfig, loggingConfig, json);
  305. }
  306. /* eslint-enable max-params */