Pier Angelo Vendrame pushed to branch tor-browser-128.6.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
0b42b720
by Henry Wilkes at 2025-01-20T17:57:59+00:00
-
7129f408
by Henry Wilkes at 2025-01-20T17:57:59+00:00
-
6ea6bd74
by Henry Wilkes at 2025-01-20T17:57:59+00:00
4 changed files:
- toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/modules/Moat.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
... | ... | @@ -227,7 +227,7 @@ export class TorProvider { |
227 | 227 | if (this.ownsTorDaemon) {
|
228 | 228 | try {
|
229 | 229 | await lazy.TorSettings.initializedPromise;
|
230 | - await this.writeSettings(lazy.TorSettings.getSettings());
|
|
230 | + await this.writeSettings();
|
|
231 | 231 | } catch (e) {
|
232 | 232 | logger.warn(
|
233 | 233 | "Failed to initialize TorSettings or to write our initial settings. Continuing the initialization anyway.",
|
... | ... | @@ -269,11 +269,13 @@ export class TorProvider { |
269 | 269 | /**
|
270 | 270 | * Send settings to the tor daemon.
|
271 | 271 | *
|
272 | - * @param {object} settings A settings object, as returned by
|
|
273 | - * TorSettings.getSettings(). This allow to try settings without passing
|
|
274 | - * through TorSettings.
|
|
272 | + * This should only be called internally or by the TorSettings module.
|
|
275 | 273 | */
|
276 | - async writeSettings(settings) {
|
|
274 | + async writeSettings() {
|
|
275 | + // Fetch the current settings.
|
|
276 | + // We set the useTemporary parameter since we want to apply temporary
|
|
277 | + // bridges if they are available.
|
|
278 | + const settings = lazy.TorSettings.getSettings(true);
|
|
277 | 279 | logger.debug("TorProvider.writeSettings", settings);
|
278 | 280 | const torSettings = new Map();
|
279 | 281 |
... | ... | @@ -13,7 +13,6 @@ ChromeUtils.defineESModuleGetters(lazy, { |
13 | 13 | DomainFrontRequestBuilder:
|
14 | 14 | "resource://gre/modules/DomainFrontedRequests.sys.mjs",
|
15 | 15 | TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs",
|
16 | - TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
|
|
17 | 16 | });
|
18 | 17 | |
19 | 18 | const TorLauncherPrefs = Object.freeze({
|
... | ... | @@ -73,6 +72,25 @@ class InternetTestResponseListener { |
73 | 72 | }
|
74 | 73 | }
|
75 | 74 | |
75 | +/**
|
|
76 | + * @typedef {Object} MoatBridges
|
|
77 | + *
|
|
78 | + * Bridge settings that can be passed to TorSettings.bridges.
|
|
79 | + *
|
|
80 | + * @property {number} source - The `TorBridgeSource` type.
|
|
81 | + * @property {string} [builtin_type] - The built-in bridge type.
|
|
82 | + * @property {string[]} [bridge_strings] - The bridge lines.
|
|
83 | + */
|
|
84 | + |
|
85 | +/**
|
|
86 | + * @typedef {Object} MoatSettings
|
|
87 | + *
|
|
88 | + * The settings returned by Moat.
|
|
89 | + *
|
|
90 | + * @property {MoatBridges[]} bridgesList - The list of bridges found.
|
|
91 | + * @property {string} [country] - The detected country (region).
|
|
92 | + */
|
|
93 | + |
|
76 | 94 | /**
|
77 | 95 | * Constructs JSON objects and sends requests over Moat.
|
78 | 96 | * The documentation about the JSON schemas to use are available at
|
... | ... | @@ -213,37 +231,31 @@ export class MoatRPC { |
213 | 231 | return { bridges, qrcode: qrcodeImg };
|
214 | 232 | }
|
215 | 233 | |
216 | - // Convert received settings object to format used by TorSettings module.
|
|
217 | - #fixupSettings(settings) {
|
|
234 | + /**
|
|
235 | + * Extract bridges from the received Moat settings object.
|
|
236 | + *
|
|
237 | + * @param {Object} settings - The received settings.
|
|
238 | + * @return {MoatBridge} The extracted bridges.
|
|
239 | + */
|
|
240 | + #extractBridges(settings) {
|
|
218 | 241 | if (!("bridges" in settings)) {
|
219 | 242 | throw new Error("Expected to find `bridges` in the settings object.");
|
220 | 243 | }
|
221 | - const retval = {
|
|
222 | - bridges: {
|
|
223 | - enabled: true,
|
|
224 | - },
|
|
225 | - };
|
|
244 | + const bridges = {};
|
|
226 | 245 | switch (settings.bridges.source) {
|
227 | 246 | case "builtin":
|
228 | - retval.bridges.source = lazy.TorBridgeSource.BuiltIn;
|
|
229 | - retval.bridges.builtin_type = settings.bridges.type;
|
|
230 | - // TorSettings will ignore strings for built-in bridges, and use the
|
|
231 | - // ones it already knows, instead. However, when we try these settings
|
|
232 | - // in the connect assist, we skip TorSettings. Therefore, we set the
|
|
233 | - // lines also here (the ones we already know, not the ones we receive
|
|
234 | - // from Moat). This needs TorSettings to be initialized, which by now
|
|
235 | - // should have already happened (this method is used only by TorConnect,
|
|
236 | - // that needs TorSettings to be initialized).
|
|
237 | - // In any case, getBuiltinBridges will throw if the data is not ready,
|
|
238 | - // yet.
|
|
239 | - retval.bridges.bridge_strings = lazy.TorSettings.getBuiltinBridges(
|
|
240 | - settings.bridges.type
|
|
241 | - );
|
|
247 | + bridges.source = lazy.TorBridgeSource.BuiltIn;
|
|
248 | + bridges.builtin_type = String(settings.bridges.type);
|
|
249 | + // Ignore the bridge_strings argument since we will use our built-in
|
|
250 | + // bridge strings instead.
|
|
242 | 251 | break;
|
243 | 252 | case "bridgedb":
|
244 | - retval.bridges.source = lazy.TorBridgeSource.BridgeDB;
|
|
245 | - if (settings.bridges.bridge_strings) {
|
|
246 | - retval.bridges.bridge_strings = settings.bridges.bridge_strings;
|
|
253 | + bridges.source = lazy.TorBridgeSource.BridgeDB;
|
|
254 | + if (settings.bridges.bridge_strings?.length) {
|
|
255 | + bridges.bridge_strings = Array.from(
|
|
256 | + settings.bridges.bridge_strings,
|
|
257 | + item => String(item)
|
|
258 | + );
|
|
247 | 259 | } else {
|
248 | 260 | throw new Error(
|
249 | 261 | "Received no bridge-strings for BridgeDB bridge source"
|
... | ... | @@ -255,37 +267,38 @@ export class MoatRPC { |
255 | 267 | `Unexpected bridge source '${settings.bridges.source}'`
|
256 | 268 | );
|
257 | 269 | }
|
258 | - return retval;
|
|
270 | + return bridges;
|
|
259 | 271 | }
|
260 | 272 | |
261 | - // Converts a list of settings objects received from BridgeDB to a list of
|
|
262 | - // settings objects understood by the TorSettings module.
|
|
263 | - // In the event of error, returns an empty list.
|
|
264 | - #fixupSettingsList(settingsList) {
|
|
265 | - const retval = [];
|
|
273 | + /**
|
|
274 | + * Extract a list of bridges from the received Moat settings object.
|
|
275 | + *
|
|
276 | + * @param {Object} settings - The received settings.
|
|
277 | + * @return {MoatBridge[]} The list of extracted bridges.
|
|
278 | + */
|
|
279 | + #extractBridgesList(settingsList) {
|
|
280 | + const bridgesList = [];
|
|
266 | 281 | for (const settings of settingsList) {
|
267 | 282 | try {
|
268 | - retval.push(this.#fixupSettings(settings));
|
|
283 | + bridgesList.push(this.#extractBridges(settings));
|
|
269 | 284 | } catch (ex) {
|
270 | 285 | log.error(ex);
|
271 | 286 | }
|
272 | 287 | }
|
273 | - return retval;
|
|
288 | + return bridgesList;
|
|
274 | 289 | }
|
275 | 290 | |
276 | - // Request tor settings for the user optionally based on their location
|
|
277 | - // (derived from their IP). Takes the following parameters:
|
|
278 | - // - transports: optional, an array of transports available to the client; if
|
|
279 | - // empty (or not given) returns settings using all working transports known
|
|
280 | - // to the server
|
|
281 | - // - country: optional, an ISO 3166-1 alpha-2 country code to request settings
|
|
282 | - // for; if not provided the country is determined by the user's IP address
|
|
283 | - //
|
|
284 | - // Returns an object with the detected country code and an array of settings
|
|
285 | - // in a format that can be passed to the TorSettings module. This array might
|
|
286 | - // be empty if the country has no associated settings.
|
|
287 | - // If the server cannot determine the user's country (and no country code is
|
|
288 | - // provided), then null is returned instead of the object.
|
|
291 | + /**
|
|
292 | + * Request tor settings for the user optionally based on their location
|
|
293 | + * (derived from their IP). Takes the following parameters:
|
|
294 | + *
|
|
295 | + * @param {string[]} transports - A list of transports we support.
|
|
296 | + * @param {?string} country - The region to request bridges for, as an
|
|
297 | + * ISO 3166-1 alpha-2 region code, or `null` to have the server
|
|
298 | + * automatically determine the region.
|
|
299 | + * @returns {?MoatSettings} - The returned settings from the server, or `null`
|
|
300 | + * if the region could not be determined by the server.
|
|
301 | + */
|
|
289 | 302 | async circumvention_settings(transports, country) {
|
290 | 303 | const args = {
|
291 | 304 | transports: transports ? transports : [],
|
... | ... | @@ -306,7 +319,7 @@ export class MoatRPC { |
306 | 319 | |
307 | 320 | throw new Error(`MoatRPC: ${detail} (${code})`);
|
308 | 321 | } else if ("settings" in response) {
|
309 | - settings.settings = this.#fixupSettingsList(response.settings);
|
|
322 | + settings.bridgesList = this.#extractBridgesList(response.settings);
|
|
310 | 323 | }
|
311 | 324 | if ("country" in response) {
|
312 | 325 | settings.country = response.country;
|
... | ... | @@ -349,14 +362,12 @@ export class MoatRPC { |
349 | 362 | return map;
|
350 | 363 | }
|
351 | 364 | |
352 | - // Request a copy of the defaul/fallback bridge settings, takes the following
|
|
353 | - // parameters:
|
|
354 | - // - transports: optional, an array of transports available to the client; if
|
|
355 | - // empty (or not given) returns settings using all working transports known
|
|
356 | - // to the server
|
|
357 | - //
|
|
358 | - // returns an array of settings objects in roughly the same format as the
|
|
359 | - // _settings object on the TorSettings module
|
|
365 | + /**
|
|
366 | + * Request a copy of the default/fallback bridge settings.
|
|
367 | + *
|
|
368 | + * @param {string[]} transports - A list of transports we support.
|
|
369 | + * @returns {MoatBridges[]} - The list of bridges found.
|
|
370 | + */
|
|
360 | 371 | async circumvention_defaults(transports) {
|
361 | 372 | const args = {
|
362 | 373 | transports: transports ? transports : [],
|
... | ... | @@ -367,7 +378,7 @@ export class MoatRPC { |
367 | 378 | const detail = response.errors[0].detail;
|
368 | 379 | throw new Error(`MoatRPC: ${detail} (${code})`);
|
369 | 380 | } else if ("settings" in response) {
|
370 | - return this.#fixupSettingsList(response.settings);
|
|
381 | + return this.#extractBridgesList(response.settings);
|
|
371 | 382 | }
|
372 | 383 | return [];
|
373 | 384 | }
|
... | ... | @@ -103,11 +103,12 @@ export const TorConnectTopics = Object.freeze({ |
103 | 103 | * failing bootstrap.
|
104 | 104 | * @property {integer} [options.simulateDelay] - The delay in microseconds to
|
105 | 105 | * apply to simulated bootstraps.
|
106 | - * @property {object} [options.simulateMoatResponse] - Simulate a Moat response
|
|
107 | - * for circumvention settings. Should include a "settings" property, and
|
|
108 | - * optionally a "country" property. You may add a "simulateCensorship"
|
|
109 | - * property to some of the settings to make only their bootstrap attempts
|
|
110 | - * fail.
|
|
106 | + * @property {MoatSettings} [options.simulateMoatResponse] - Simulate a Moat
|
|
107 | + * response for circumvention settings. Should include a "bridgesList"
|
|
108 | + * property, and optionally a "country" property. The "bridgesList" property
|
|
109 | + * should be an Array of MoatBridges objects that match the bridge settings
|
|
110 | + * accepted by TorSettings.bridges, plus you may add a "simulateCensorship"
|
|
111 | + * property to make only their bootstrap attempts fail.
|
|
111 | 112 | * @property {boolean} [options.testInternet] - Whether to also test the
|
112 | 113 | * internet connection.
|
113 | 114 | * @property {boolean} [options.simulateOffline] - Whether to simulate an
|
... | ... | @@ -214,6 +215,14 @@ class BootstrapAttempt { |
214 | 215 | return promise;
|
215 | 216 | }
|
216 | 217 | |
218 | + /**
|
|
219 | + * Method to call just after the Bootstrapped stage is set in response to this
|
|
220 | + * bootstrap attempt.
|
|
221 | + */
|
|
222 | + postBootstrapped() {
|
|
223 | + // Nothing to do.
|
|
224 | + }
|
|
225 | + |
|
217 | 226 | /**
|
218 | 227 | * Run the attempt.
|
219 | 228 | *
|
... | ... | @@ -395,17 +404,11 @@ class AutoBootstrapAttempt { |
395 | 404 | */
|
396 | 405 | #cancelledPromise = null;
|
397 | 406 | /**
|
398 | - * The found settings from Moat.
|
|
399 | - *
|
|
400 | - * @type {?object[]}
|
|
401 | - */
|
|
402 | - #settings = null;
|
|
403 | - /**
|
|
404 | - * The last settings that have been applied to the TorProvider, if any.
|
|
407 | + * The list of bridge configurations from Moat.
|
|
405 | 408 | *
|
406 | - * @type {?object}
|
|
409 | + * @type {?MoatBridges[]}
|
|
407 | 410 | */
|
408 | - #changedSetting = null;
|
|
411 | + #bridgesList = null;
|
|
409 | 412 | /**
|
410 | 413 | * The detected region code returned by Moat, if any.
|
411 | 414 | *
|
... | ... | @@ -438,13 +441,8 @@ class AutoBootstrapAttempt { |
438 | 441 | // Run cleanup before we resolve the promise to ensure two instances
|
439 | 442 | // of AutoBootstrapAttempt are not trying to change the settings at
|
440 | 443 | // the same time.
|
441 | - if (this.#changedSetting) {
|
|
442 | - if (arg.result === "complete") {
|
|
443 | - // Persist the current settings to preferences.
|
|
444 | - lazy.TorSettings.setSettings(this.#changedSetting);
|
|
445 | - lazy.TorSettings.saveToPrefs();
|
|
446 | - } // else, applySettings will restore the current settings.
|
|
447 | - await lazy.TorSettings.applySettings();
|
|
444 | + if (arg.result !== "complete") {
|
|
445 | + await lazy.TorSettings.clearTemporaryBridges();
|
|
448 | 446 | }
|
449 | 447 | } catch (error) {
|
450 | 448 | lazy.logger.error("Unexpected error in auto-bootstrap cleanup", error);
|
... | ... | @@ -466,6 +464,15 @@ class AutoBootstrapAttempt { |
466 | 464 | return promise;
|
467 | 465 | }
|
468 | 466 | |
467 | + /**
|
|
468 | + * Method to call just after the Bootstrapped stage is set in response to this
|
|
469 | + * bootstrap attempt.
|
|
470 | + */
|
|
471 | + postBootstrapped() {
|
|
472 | + // Persist the current settings to preferences.
|
|
473 | + lazy.TorSettings.saveTemporaryBridges();
|
|
474 | + }
|
|
475 | + |
|
469 | 476 | /**
|
470 | 477 | * Run the attempt.
|
471 | 478 | *
|
... | ... | @@ -477,12 +484,12 @@ class AutoBootstrapAttempt { |
477 | 484 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
478 | 485 | */
|
479 | 486 | async #runInternal(progressCallback, options) {
|
480 | - await this.#fetchSettings(options);
|
|
487 | + await this.#fetchBridges(options);
|
|
481 | 488 | if (this.#cancelled || this.#resolved) {
|
482 | 489 | return;
|
483 | 490 | }
|
484 | 491 | |
485 | - if (!this.#settings?.length) {
|
|
492 | + if (!this.#bridgesList?.length) {
|
|
486 | 493 | this.#resolveRun({
|
487 | 494 | error: new TorConnectError(
|
488 | 495 | options.regionCode === "automatic" && !this.detectedRegion
|
... | ... | @@ -493,14 +500,14 @@ class AutoBootstrapAttempt { |
493 | 500 | }
|
494 | 501 | |
495 | 502 | // Apply each of our settings and try to bootstrap with each.
|
496 | - for (const [index, currentSetting] of this.#settings.entries()) {
|
|
503 | + for (const [index, bridges] of this.#bridgesList.entries()) {
|
|
497 | 504 | lazy.logger.info(
|
498 | 505 | `Attempting Bootstrap with configuration ${index + 1}/${
|
499 | - this.#settings.length
|
|
506 | + this.#bridgesList.length
|
|
500 | 507 | }`
|
501 | 508 | );
|
502 | 509 | |
503 | - await this.#trySetting(currentSetting, progressCallback, options);
|
|
510 | + await this.#tryBridges(bridges, progressCallback, options);
|
|
504 | 511 | |
505 | 512 | if (this.#cancelled || this.#resolved) {
|
506 | 513 | return;
|
... | ... | @@ -518,7 +525,7 @@ class AutoBootstrapAttempt { |
518 | 525 | *
|
519 | 526 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
520 | 527 | */
|
521 | - async #fetchSettings(options) {
|
|
528 | + async #fetchBridges(options) {
|
|
522 | 529 | if (options.simulateMoatResponse) {
|
523 | 530 | await Promise.race([
|
524 | 531 | new Promise(res => setTimeout(res, options.simulateDelay || 0)),
|
... | ... | @@ -530,7 +537,7 @@ class AutoBootstrapAttempt { |
530 | 537 | }
|
531 | 538 | |
532 | 539 | this.detectedRegion = options.simulateMoatResponse.country || null;
|
533 | - this.#settings = options.simulateMoatResponse.settings ?? null;
|
|
540 | + this.#bridgesList = options.simulateMoatResponse.bridgesList ?? null;
|
|
534 | 541 | |
535 | 542 | return;
|
536 | 543 | }
|
... | ... | @@ -564,16 +571,16 @@ class AutoBootstrapAttempt { |
564 | 571 | |
565 | 572 | this.detectedRegion = maybeSettings?.country || null;
|
566 | 573 | |
567 | - if (maybeSettings?.settings?.length) {
|
|
568 | - this.#settings = maybeSettings.settings;
|
|
574 | + if (maybeSettings?.bridgesList?.length) {
|
|
575 | + this.#bridgesList = maybeSettings.bridgesList;
|
|
569 | 576 | } else {
|
570 | 577 | // Keep consistency with the other call.
|
571 | - this.#settings = await Promise.race([
|
|
578 | + this.#bridgesList = await Promise.race([
|
|
572 | 579 | moat.circumvention_defaults([
|
573 | 580 | ...lazy.TorSettings.builtinBridgeTypes,
|
574 | 581 | "vanilla",
|
575 | 582 | ]),
|
576 | - // This might set this.#settings to undefined.
|
|
583 | + // This might set this.#bridgesList to undefined.
|
|
577 | 584 | this.#cancelledPromise,
|
578 | 585 | ]);
|
579 | 586 | }
|
... | ... | @@ -586,21 +593,21 @@ class AutoBootstrapAttempt { |
586 | 593 | /**
|
587 | 594 | * Try to apply the settings we fetched.
|
588 | 595 | *
|
589 | - * @param {object} setting - The setting to try.
|
|
596 | + * @param {MoatBridges} bridges - The bridges to try.
|
|
590 | 597 | * @param {ProgressCallback} progressCallback - The callback to invoke with
|
591 | 598 | * the bootstrap progress.
|
592 | 599 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
593 | 600 | */
|
594 | - async #trySetting(setting, progressCallback, options) {
|
|
601 | + async #tryBridges(bridges, progressCallback, options) {
|
|
595 | 602 | if (this.#cancelled || this.#resolved) {
|
596 | 603 | return;
|
597 | 604 | }
|
598 | 605 | |
599 | - if (options.simulateMoatResponse && setting.simulateCensorship) {
|
|
606 | + if (options.simulateMoatResponse && bridges.simulateCensorship) {
|
|
600 | 607 | // Move the simulateCensorship option to the options for the next
|
601 | 608 | // BootstrapAttempt.
|
602 | - setting = structuredClone(setting);
|
|
603 | - delete setting.simulateCensorship;
|
|
609 | + bridges = structuredClone(bridges);
|
|
610 | + delete bridges.simulateCensorship;
|
|
604 | 611 | options = { ...options, simulateCensorship: true };
|
605 | 612 | }
|
606 | 613 | |
... | ... | @@ -616,14 +623,7 @@ class AutoBootstrapAttempt { |
616 | 623 | // Another idea (maybe easier to implement) is to disable the settings
|
617 | 624 | // UI while *any* bootstrap is going on.
|
618 | 625 | // This is also documented in tor-browser#41921.
|
619 | - const provider = await lazy.TorProviderBuilder.build();
|
|
620 | - this.#changedSetting = setting;
|
|
621 | - // We need to merge with old settings, in case the user is using a proxy
|
|
622 | - // or is behind a firewall.
|
|
623 | - await provider.writeSettings({
|
|
624 | - ...lazy.TorSettings.getSettings(),
|
|
625 | - ...setting,
|
|
626 | - });
|
|
626 | + await lazy.TorSettings.applyTemporaryBridges(bridges);
|
|
627 | 627 | |
628 | 628 | if (this.#cancelled || this.#resolved) {
|
629 | 629 | return;
|
... | ... | @@ -642,7 +642,7 @@ class AutoBootstrapAttempt { |
642 | 642 | error instanceof TorConnectError &&
|
643 | 643 | error.code === TorConnectError.BootstrapError
|
644 | 644 | ) {
|
645 | - lazy.logger.info("TorConnect setting failed", setting, error);
|
|
645 | + lazy.logger.info("TorConnect setting failed", bridges, error);
|
|
646 | 646 | // Try with the next settings.
|
647 | 647 | // NOTE: We do not restore the user settings in between these runs.
|
648 | 648 | // Instead we wait for #resolveRun callback to do so.
|
... | ... | @@ -1459,6 +1459,12 @@ export const TorConnect = { |
1459 | 1459 | }
|
1460 | 1460 | this._setStage(TorConnectStage.Bootstrapped);
|
1461 | 1461 | Services.obs.notifyObservers(null, TorConnectTopics.BootstrapComplete);
|
1462 | + |
|
1463 | + // Now call the postBootstrapped method. We do this after changing the
|
|
1464 | + // stage to ensure that AutoBootstrapAttempt.postBootstrapped call to
|
|
1465 | + // saveTemporaryBridges does not trigger SettingsChanged too early and
|
|
1466 | + // cancel itself.
|
|
1467 | + bootstrapAttempt.postBootstrapped();
|
|
1462 | 1468 | return;
|
1463 | 1469 | }
|
1464 | 1470 |
... | ... | @@ -198,6 +198,14 @@ class TorSettingsImpl { |
198 | 198 | allowed_ports: [],
|
199 | 199 | },
|
200 | 200 | };
|
201 | + |
|
202 | + /**
|
|
203 | + * Temporary bridge settings to apply instead of #settings.bridges.
|
|
204 | + *
|
|
205 | + * @type {?Object}
|
|
206 | + */
|
|
207 | + #temporaryBridgeSettings = null;
|
|
208 | + |
|
201 | 209 | /**
|
202 | 210 | * Accumulated errors from trying to set settings.
|
203 | 211 | *
|
... | ... | @@ -713,6 +721,8 @@ class TorSettingsImpl { |
713 | 721 | try {
|
714 | 722 | this.#allowUninitialized = true;
|
715 | 723 | this.#loadFromPrefs();
|
724 | + // We do not pass on the loaded settings to the TorProvider yet. Instead
|
|
725 | + // TorProvider will ask for these once it has initialised.
|
|
716 | 726 | } finally {
|
717 | 727 | this.#allowUninitialized = false;
|
718 | 728 | this.#notificationQueue.clear();
|
... | ... | @@ -971,7 +981,7 @@ class TorSettingsImpl { |
971 | 981 | async applySettings() {
|
972 | 982 | this.#checkIfInitialized();
|
973 | 983 | const provider = await lazy.TorProviderBuilder.build();
|
974 | - await provider.writeSettings(this.getSettings());
|
|
984 | + await provider.writeSettings();
|
|
975 | 985 | }
|
976 | 986 | |
977 | 987 | /**
|
... | ... | @@ -1073,12 +1083,20 @@ class TorSettingsImpl { |
1073 | 1083 | /**
|
1074 | 1084 | * Get a copy of all our settings.
|
1075 | 1085 | *
|
1086 | + * @param {boolean} [useTemporary=false] - Whether the returned settings
|
|
1087 | + * should use the temporary bridge settings, if any, instead.
|
|
1088 | + *
|
|
1076 | 1089 | * @returns {object} A copy of the settings object
|
1077 | 1090 | */
|
1078 | - getSettings() {
|
|
1091 | + getSettings(useTemporary = false) {
|
|
1079 | 1092 | lazy.logger.debug("getSettings()");
|
1080 | 1093 | this.#checkIfInitialized();
|
1081 | - return structuredClone(this.#settings);
|
|
1094 | + const settings = structuredClone(this.#settings);
|
|
1095 | + if (useTemporary && this.#temporaryBridgeSettings) {
|
|
1096 | + // Override the bridge settings with our temporary ones.
|
|
1097 | + settings.bridges = structuredClone(this.#temporaryBridgeSettings);
|
|
1098 | + }
|
|
1099 | + return settings;
|
|
1082 | 1100 | }
|
1083 | 1101 | |
1084 | 1102 | /**
|
... | ... | @@ -1097,6 +1115,83 @@ class TorSettingsImpl { |
1097 | 1115 | }
|
1098 | 1116 | return types;
|
1099 | 1117 | }
|
1118 | + |
|
1119 | + /**
|
|
1120 | + * Apply some Moat bridges temporarily.
|
|
1121 | + *
|
|
1122 | + * These bridges will not yet be saved to settings.
|
|
1123 | + *
|
|
1124 | + * @param {MoatBridges} bridges - The bridges to apply.
|
|
1125 | + */
|
|
1126 | + async applyTemporaryBridges(bridges) {
|
|
1127 | + this.#checkIfInitialized();
|
|
1128 | + |
|
1129 | + if (
|
|
1130 | + bridges.source !== TorBridgeSource.BuiltIn &&
|
|
1131 | + bridges.source !== TorBridgeSource.BridgeDB
|
|
1132 | + ) {
|
|
1133 | + throw new Error(`Invalid bridge source ${bridges.source}`);
|
|
1134 | + }
|
|
1135 | + |
|
1136 | + const bridgeSettings = {
|
|
1137 | + enabled: true,
|
|
1138 | + source: bridges.source,
|
|
1139 | + };
|
|
1140 | + |
|
1141 | + if (bridges.source === TorBridgeSource.BuiltIn) {
|
|
1142 | + if (!bridges.builtin_type) {
|
|
1143 | + throw Error("Missing a built-in type");
|
|
1144 | + }
|
|
1145 | + bridgeSettings.builtin_type = String(bridges.builtin_type);
|
|
1146 | + const bridgeStrings = this.getBuiltinBridges(bridgeSettings.builtin_type);
|
|
1147 | + if (!bridgeStrings.length) {
|
|
1148 | + throw new Error(`No builtin bridges for type ${bridges.builtin_type}`);
|
|
1149 | + }
|
|
1150 | + bridgeSettings.bridge_strings = bridgeStrings;
|
|
1151 | + } else {
|
|
1152 | + // BridgeDB.
|
|
1153 | + if (!bridges.bridge_strings?.length) {
|
|
1154 | + throw new Error("Missing bridges strings");
|
|
1155 | + }
|
|
1156 | + // TODO: Can we safely verify the format of the bridge addresses sent from
|
|
1157 | + // Moat?
|
|
1158 | + bridgeSettings.bridge_strings = Array.from(bridges.bridge_strings, item =>
|
|
1159 | + String(item)
|
|
1160 | + );
|
|
1161 | + }
|
|
1162 | + |
|
1163 | + // After checks are complete, we commit them.
|
|
1164 | + this.#temporaryBridgeSettings = bridgeSettings;
|
|
1165 | + await this.applySettings();
|
|
1166 | + }
|
|
1167 | + |
|
1168 | + /**
|
|
1169 | + * Save to current temporary bridges to be permanent instead.
|
|
1170 | + */
|
|
1171 | + async saveTemporaryBridges() {
|
|
1172 | + this.#checkIfInitialized();
|
|
1173 | + if (!this.#temporaryBridgeSettings) {
|
|
1174 | + lazy.logger.warn("No temporary bridges to save");
|
|
1175 | + return;
|
|
1176 | + }
|
|
1177 | + this.setSettings({ bridges: this.#temporaryBridgeSettings });
|
|
1178 | + this.#temporaryBridgeSettings = null;
|
|
1179 | + this.saveToPrefs();
|
|
1180 | + await this.applySettings();
|
|
1181 | + }
|
|
1182 | + |
|
1183 | + /**
|
|
1184 | + * Clear the current temporary bridges.
|
|
1185 | + */
|
|
1186 | + async clearTemporaryBridges() {
|
|
1187 | + this.#checkIfInitialized();
|
|
1188 | + if (!this.#temporaryBridgeSettings) {
|
|
1189 | + lazy.logger.debug("No temporary bridges to clear");
|
|
1190 | + return;
|
|
1191 | + }
|
|
1192 | + this.#temporaryBridgeSettings = null;
|
|
1193 | + await this.applySettings();
|
|
1194 | + }
|
|
1100 | 1195 | }
|
1101 | 1196 | |
1102 | 1197 | export const TorSettings = new TorSettingsImpl(); |