Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

functions.any.js 13KB

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