... |
... |
@@ -179,10 +179,34 @@ class TorSettingsImpl { |
179
|
179
|
enabled: false,
|
180
|
180
|
},
|
181
|
181
|
bridges: {
|
|
182
|
+ /**
|
|
183
|
+ * Whether the bridges are enabled or not.
|
|
184
|
+ *
|
|
185
|
+ * @type {boolean}
|
|
186
|
+ */
|
182
|
187
|
enabled: false,
|
183
|
188
|
source: TorBridgeSource.Invalid,
|
|
189
|
+ /**
|
|
190
|
+ * The lox id is used with the Lox "source", and remains set with the
|
|
191
|
+ * stored value when other sources are used.
|
|
192
|
+ *
|
|
193
|
+ * @type {string}
|
|
194
|
+ */
|
184
|
195
|
lox_id: "",
|
|
196
|
+ /**
|
|
197
|
+ * The built-in type to use when using the BuiltIn "source", or empty when
|
|
198
|
+ * using any other source.
|
|
199
|
+ *
|
|
200
|
+ * @type {string}
|
|
201
|
+ */
|
185
|
202
|
builtin_type: "",
|
|
203
|
+ /**
|
|
204
|
+ * The current bridge strings.
|
|
205
|
+ *
|
|
206
|
+ * Can only be non-empty if the "source" is not Invalid.
|
|
207
|
+ *
|
|
208
|
+ * @type {Array<string>}
|
|
209
|
+ */
|
186
|
210
|
bridge_strings: [],
|
187
|
211
|
},
|
188
|
212
|
proxy: {
|
... |
... |
@@ -206,15 +230,6 @@ class TorSettingsImpl { |
206
|
230
|
*/
|
207
|
231
|
#temporaryBridgeSettings = null;
|
208
|
232
|
|
209
|
|
- /**
|
210
|
|
- * Accumulated errors from trying to set settings.
|
211
|
|
- *
|
212
|
|
- * Only added to if not null.
|
213
|
|
- *
|
214
|
|
- * @type {Array<Error>?}
|
215
|
|
- */
|
216
|
|
- #settingErrors = null;
|
217
|
|
-
|
218
|
233
|
/**
|
219
|
234
|
* The recommended pluggable transport.
|
220
|
235
|
*
|
... |
... |
@@ -245,13 +260,6 @@ class TorSettingsImpl { |
245
|
260
|
* @type {boolean}
|
246
|
261
|
*/
|
247
|
262
|
#initialized = false;
|
248
|
|
- /**
|
249
|
|
- * During some phases of the initialization, allow calling setters and
|
250
|
|
- * getters without throwing errors.
|
251
|
|
- *
|
252
|
|
- * @type {boolean}
|
253
|
|
- */
|
254
|
|
- #allowUninitialized = false;
|
255
|
263
|
|
256
|
264
|
constructor() {
|
257
|
265
|
this.initializedPromise = new Promise((resolve, reject) => {
|
... |
... |
@@ -259,347 +267,57 @@ class TorSettingsImpl { |
259
|
267
|
this.#initFailed = reject;
|
260
|
268
|
});
|
261
|
269
|
|
262
|
|
- this.#addProperties("quickstart", {
|
263
|
|
- enabled: {},
|
264
|
|
- });
|
265
|
|
- this.#addProperties("bridges", {
|
266
|
|
- /**
|
267
|
|
- * Whether the bridges are enabled or not.
|
268
|
|
- *
|
269
|
|
- * @type {boolean}
|
270
|
|
- */
|
271
|
|
- enabled: {},
|
272
|
|
- /**
|
273
|
|
- * The current bridge source.
|
274
|
|
- *
|
275
|
|
- * @type {integer}
|
276
|
|
- */
|
277
|
|
- source: {
|
278
|
|
- transform: (val, addError) => {
|
279
|
|
- if (Object.values(TorBridgeSource).includes(val)) {
|
280
|
|
- return val;
|
281
|
|
- }
|
282
|
|
- addError(`Not a valid bridge source: "${val}"`);
|
283
|
|
- return TorBridgeSource.Invalid;
|
284
|
|
- },
|
285
|
|
- },
|
286
|
|
- /**
|
287
|
|
- * The current bridge strings.
|
288
|
|
- *
|
289
|
|
- * Can only be non-empty if the "source" is not Invalid.
|
290
|
|
- *
|
291
|
|
- * @type {Array<string>}
|
292
|
|
- */
|
293
|
|
- bridge_strings: {
|
294
|
|
- transform: val => {
|
295
|
|
- if (Array.isArray(val)) {
|
296
|
|
- return [...val];
|
297
|
|
- }
|
298
|
|
- // Split the bridge strings, discarding empty.
|
299
|
|
- return splitBridgeLines(val).filter(val => val);
|
300
|
|
- },
|
301
|
|
- copy: val => [...val],
|
302
|
|
- equal: (val1, val2) => this.#arrayEqual(val1, val2),
|
303
|
|
- },
|
304
|
|
- /**
|
305
|
|
- * The built-in type to use when using the BuiltIn "source", or empty when
|
306
|
|
- * using any other source.
|
307
|
|
- *
|
308
|
|
- * @type {string}
|
309
|
|
- */
|
310
|
|
- builtin_type: {
|
311
|
|
- callback: (val, addError) => {
|
312
|
|
- if (!val) {
|
313
|
|
- return;
|
314
|
|
- }
|
315
|
|
- const bridgeStrings = this.getBuiltinBridges(val);
|
316
|
|
- if (bridgeStrings.length) {
|
317
|
|
- this.bridges.bridge_strings = bridgeStrings;
|
318
|
|
- return;
|
319
|
|
- }
|
320
|
|
-
|
321
|
|
- addError(`No built-in ${val} bridges found`);
|
322
|
|
- // Set as invalid, which will make the builtin_type "" and set the
|
323
|
|
- // bridge_strings to be empty at the next #cleanupSettings.
|
324
|
|
- this.bridges.source = TorBridgeSource.Invalid;
|
325
|
|
- },
|
326
|
|
- },
|
327
|
|
- /**
|
328
|
|
- * The lox id is used with the Lox "source", and remains set with the stored value when
|
329
|
|
- * other sources are used.
|
330
|
|
- *
|
331
|
|
- * @type {string}
|
332
|
|
- */
|
333
|
|
- lox_id: {
|
334
|
|
- callback: (val, addError) => {
|
335
|
|
- if (!val) {
|
336
|
|
- return;
|
337
|
|
- }
|
338
|
|
- let bridgeStrings;
|
339
|
|
- try {
|
340
|
|
- bridgeStrings = lazy.Lox.getBridges(val);
|
341
|
|
- } catch (error) {
|
342
|
|
- addError(`No bridges for lox_id ${val}: ${error?.message}`);
|
343
|
|
- // Set as invalid, which will make the builtin_type "" and set the
|
344
|
|
- // bridge_strings to be empty at the next #cleanupSettings.
|
345
|
|
- this.bridges.source = TorBridgeSource.Invalid;
|
346
|
|
- return;
|
347
|
|
- }
|
348
|
|
- this.bridges.bridge_strings = bridgeStrings;
|
349
|
|
- },
|
350
|
|
- },
|
351
|
|
- });
|
352
|
|
- this.#addProperties("proxy", {
|
353
|
|
- enabled: {},
|
354
|
|
- type: {
|
355
|
|
- transform: (val, addError) => {
|
356
|
|
- if (Object.values(TorProxyType).includes(val)) {
|
357
|
|
- return val;
|
358
|
|
- }
|
359
|
|
- addError(`Not a valid proxy type: "${val}"`);
|
360
|
|
- return TorProxyType.Invalid;
|
361
|
|
- },
|
362
|
|
- },
|
363
|
|
- address: {},
|
364
|
|
- port: {
|
365
|
|
- transform: (val, addError) => {
|
366
|
|
- if (val === 0) {
|
367
|
|
- // This is a valid value that "unsets" the port.
|
368
|
|
- // Keep this value without giving a warning.
|
369
|
|
- // NOTE: In contrast, "0" is not valid.
|
370
|
|
- return 0;
|
371
|
|
- }
|
372
|
|
- // Unset to 0 if invalid null is returned.
|
373
|
|
- return this.#parsePort(val, false, addError) ?? 0;
|
374
|
|
- },
|
375
|
|
- },
|
376
|
|
- username: {},
|
377
|
|
- password: {},
|
378
|
|
- uri: {
|
379
|
|
- getter: () => {
|
380
|
|
- const { type, address, port, username, password } = this.proxy;
|
381
|
|
- switch (type) {
|
382
|
|
- case TorProxyType.Socks4:
|
383
|
|
- return `socks4a://${address}:${port}`;
|
384
|
|
- case TorProxyType.Socks5:
|
385
|
|
- if (username) {
|
386
|
|
- return `socks5://${username}:${password}@${address}:${port}`;
|
387
|
|
- }
|
388
|
|
- return `socks5://${address}:${port}`;
|
389
|
|
- case TorProxyType.HTTPS:
|
390
|
|
- if (username) {
|
391
|
|
- return `http://${username}:${password}@${address}:${port}`;
|
392
|
|
- }
|
393
|
|
- return `http://${address}:${port}`;
|
394
|
|
- }
|
395
|
|
- return null;
|
396
|
|
- },
|
397
|
|
- },
|
398
|
|
- });
|
399
|
|
- this.#addProperties("firewall", {
|
400
|
|
- enabled: {},
|
401
|
|
- allowed_ports: {
|
402
|
|
- transform: (val, addError) => {
|
403
|
|
- if (!Array.isArray(val)) {
|
404
|
|
- val = val === "" ? [] : val.split(",");
|
405
|
|
- }
|
406
|
|
- // parse and remove duplicates
|
407
|
|
- const portSet = new Set(
|
408
|
|
- val.map(p => this.#parsePort(p, true, addError))
|
409
|
|
- );
|
410
|
|
- // parsePort returns null for failed parses, so remove it.
|
411
|
|
- portSet.delete(null);
|
412
|
|
- return [...portSet];
|
413
|
|
- },
|
414
|
|
- copy: val => [...val],
|
415
|
|
- equal: (val1, val2) => this.#arrayEqual(val1, val2),
|
416
|
|
- },
|
417
|
|
- });
|
418
|
|
- }
|
419
|
|
-
|
420
|
|
- /**
|
421
|
|
- * Clean the setting values after making some changes, so that the values do
|
422
|
|
- * not contradict each other.
|
423
|
|
- */
|
424
|
|
- #cleanupSettings() {
|
425
|
|
- this.#freezeNotifications();
|
426
|
|
- try {
|
427
|
|
- if (this.bridges.source === TorBridgeSource.Invalid) {
|
428
|
|
- this.bridges.enabled = false;
|
429
|
|
- this.bridges.bridge_strings = [];
|
|
270
|
+ // Add some read-only getters for the #settings object.
|
|
271
|
+ // E.g. TorSetting.#settings.bridges.source is exposed publicly as
|
|
272
|
+ // TorSettings.bridges.source.
|
|
273
|
+ for (const groupname in this.#settings) {
|
|
274
|
+ const publicGroup = {};
|
|
275
|
+ for (const name in this.#settings[groupname]) {
|
|
276
|
+ // Public group only has a getter for the property.
|
|
277
|
+ Object.defineProperty(publicGroup, name, {
|
|
278
|
+ get: () => {
|
|
279
|
+ this.#checkIfInitialized();
|
|
280
|
+ return structuredClone(this.#settings[groupname][name]);
|
|
281
|
+ },
|
|
282
|
+ set: () => {
|
|
283
|
+ throw new Error(
|
|
284
|
+ `TorSettings.${groupname}.${name} cannot be set directly`
|
|
285
|
+ );
|
|
286
|
+ },
|
|
287
|
+ });
|
430
|
288
|
}
|
431
|
|
- if (!this.bridges.bridge_strings.length) {
|
432
|
|
- this.bridges.enabled = false;
|
433
|
|
- this.bridges.source = TorBridgeSource.Invalid;
|
434
|
|
- }
|
435
|
|
- if (this.bridges.source !== TorBridgeSource.BuiltIn) {
|
436
|
|
- this.bridges.builtin_type = "";
|
437
|
|
- }
|
438
|
|
- if (this.bridges.source !== TorBridgeSource.Lox) {
|
439
|
|
- this.bridges.lox_id = "";
|
440
|
|
- }
|
441
|
|
- if (!this.proxy.enabled) {
|
442
|
|
- this.proxy.type = TorProxyType.Invalid;
|
443
|
|
- this.proxy.address = "";
|
444
|
|
- this.proxy.port = 0;
|
445
|
|
- this.proxy.username = "";
|
446
|
|
- this.proxy.password = "";
|
447
|
|
- }
|
448
|
|
- if (!this.firewall.enabled) {
|
449
|
|
- this.firewall.allowed_ports = [];
|
450
|
|
- }
|
451
|
|
- } finally {
|
452
|
|
- this.#thawNotifications();
|
|
289
|
+ // The group object itself should not be writable.
|
|
290
|
+ Object.preventExtensions(publicGroup);
|
|
291
|
+ Object.defineProperty(this, groupname, {
|
|
292
|
+ writable: false,
|
|
293
|
+ value: publicGroup,
|
|
294
|
+ });
|
453
|
295
|
}
|
454
|
296
|
}
|
455
|
297
|
|
456
|
298
|
/**
|
457
|
|
- * The current number of freezes applied to the notifications.
|
458
|
|
- *
|
459
|
|
- * @type {integer}
|
460
|
|
- */
|
461
|
|
- #freezeNotificationsCount = 0;
|
462
|
|
- /**
|
463
|
|
- * The queue for settings that have changed. To be broadcast in the
|
464
|
|
- * notification when not frozen.
|
465
|
|
- *
|
466
|
|
- * @type {Set<string>}
|
467
|
|
- */
|
468
|
|
- #notificationQueue = new Set();
|
469
|
|
- /**
|
470
|
|
- * Send a notification if we have any queued and we are not frozen.
|
471
|
|
- */
|
472
|
|
- #tryNotification() {
|
473
|
|
- if (this.#freezeNotificationsCount || !this.#notificationQueue.size) {
|
474
|
|
- return;
|
475
|
|
- }
|
476
|
|
- Services.obs.notifyObservers(
|
477
|
|
- { changes: [...this.#notificationQueue] },
|
478
|
|
- TorSettingsTopics.SettingsChanged
|
479
|
|
- );
|
480
|
|
- this.#notificationQueue.clear();
|
481
|
|
- }
|
482
|
|
- /**
|
483
|
|
- * Pause notifications for changes in setting values. This is useful if you
|
484
|
|
- * need to make batch changes to settings.
|
485
|
|
- *
|
486
|
|
- * This should always be paired with a call to thawNotifications once
|
487
|
|
- * notifications should be released. Usually you should wrap whatever
|
488
|
|
- * changes you make with a `try` block and call thawNotifications in the
|
489
|
|
- * `finally` block.
|
490
|
|
- */
|
491
|
|
- #freezeNotifications() {
|
492
|
|
- this.#freezeNotificationsCount++;
|
493
|
|
- }
|
494
|
|
- /**
|
495
|
|
- * Release the hold on notifications so they may be sent out.
|
496
|
|
- *
|
497
|
|
- * Note, if some other method has also frozen the notifications, this will
|
498
|
|
- * only release them once it has also called this method.
|
499
|
|
- */
|
500
|
|
- #thawNotifications() {
|
501
|
|
- this.#freezeNotificationsCount--;
|
502
|
|
- this.#tryNotification();
|
503
|
|
- }
|
504
|
|
- /**
|
505
|
|
- * @typedef {object} TorSettingProperty
|
|
299
|
+ * The proxy URI for the current settings, or `null` if no proxy is
|
|
300
|
+ * configured.
|
506
|
301
|
*
|
507
|
|
- * @property {function} [getter] - A getter for the property. If this is
|
508
|
|
- * given, the property cannot be set.
|
509
|
|
- * @property {function} [transform] - Called in the setter for the property,
|
510
|
|
- * with the new value given. Should transform the given value into the
|
511
|
|
- * right type.
|
512
|
|
- * @property {function} [equal] - Test whether two values for the property
|
513
|
|
- * are considered equal. Otherwise uses `===`.
|
514
|
|
- * @property {function} [callback] - Called whenever the property value
|
515
|
|
- * changes, with the new value given. Should be used to trigger any other
|
516
|
|
- * required changes for the new value.
|
517
|
|
- * @property {function} [copy] - Called whenever the property is read, with
|
518
|
|
- * the stored value given. Should return a copy of the value. Otherwise
|
519
|
|
- * returns the stored value.
|
|
302
|
+ * @type {?string}
|
520
|
303
|
*/
|
521
|
|
- /**
|
522
|
|
- * Add properties to the TorSettings instance, to be read or set.
|
523
|
|
- *
|
524
|
|
- * @param {string} groupname - The name of the setting group. The given
|
525
|
|
- * settings will be accessible from the TorSettings property of the same
|
526
|
|
- * name.
|
527
|
|
- * @param {object.<string, TorSettingProperty>} propParams - An object that
|
528
|
|
- * defines the settings to add to this group. The object property names
|
529
|
|
- * will be mapped to properties of TorSettings under the given groupname
|
530
|
|
- * property. Details about the setting should be described in the
|
531
|
|
- * TorSettingProperty property value.
|
532
|
|
- */
|
533
|
|
- #addProperties(groupname, propParams) {
|
534
|
|
- // Create a new object to hold all these settings.
|
535
|
|
- const group = {};
|
536
|
|
- for (const name in propParams) {
|
537
|
|
- const { getter, transform, callback, copy, equal } = propParams[name];
|
538
|
|
- // Method for adding setting errors.
|
539
|
|
- const addError = message => {
|
540
|
|
- message = `TorSettings.${groupname}.${name}: ${message}`;
|
541
|
|
- lazy.logger.error(message);
|
542
|
|
- // Only add to #settingErrors if it is not null.
|
543
|
|
- this.#settingErrors?.push(message);
|
544
|
|
- };
|
545
|
|
- Object.defineProperty(group, name, {
|
546
|
|
- get: getter
|
547
|
|
- ? () => {
|
548
|
|
- // Allow getting in loadFromPrefs before we are initialized.
|
549
|
|
- if (!this.#allowUninitialized) {
|
550
|
|
- this.#checkIfInitialized();
|
551
|
|
- }
|
552
|
|
- return getter();
|
553
|
|
- }
|
554
|
|
- : () => {
|
555
|
|
- // Allow getting in loadFromPrefs before we are initialized.
|
556
|
|
- if (!this.#allowUninitialized) {
|
557
|
|
- this.#checkIfInitialized();
|
558
|
|
- }
|
559
|
|
- let val = this.#settings[groupname][name];
|
560
|
|
- if (copy) {
|
561
|
|
- val = copy(val);
|
562
|
|
- }
|
563
|
|
- // Assume string or number value.
|
564
|
|
- return val;
|
565
|
|
- },
|
566
|
|
- set: getter
|
567
|
|
- ? undefined
|
568
|
|
- : val => {
|
569
|
|
- // Allow setting in loadFromPrefs before we are initialized.
|
570
|
|
- if (!this.#allowUninitialized) {
|
571
|
|
- this.#checkIfInitialized();
|
572
|
|
- }
|
573
|
|
- const prevVal = this.#settings[groupname][name];
|
574
|
|
- this.#freezeNotifications();
|
575
|
|
- try {
|
576
|
|
- if (transform) {
|
577
|
|
- val = transform(val, addError);
|
578
|
|
- }
|
579
|
|
- const isEqual = equal ? equal(val, prevVal) : val === prevVal;
|
580
|
|
- if (!isEqual) {
|
581
|
|
- // Set before the callback.
|
582
|
|
- this.#settings[groupname][name] = val;
|
583
|
|
- this.#notificationQueue.add(`${groupname}.${name}`);
|
584
|
|
-
|
585
|
|
- if (callback) {
|
586
|
|
- callback(val, addError);
|
587
|
|
- }
|
588
|
|
- }
|
589
|
|
- } catch (e) {
|
590
|
|
- addError(e.message);
|
591
|
|
- } finally {
|
592
|
|
- this.#thawNotifications();
|
593
|
|
- }
|
594
|
|
- },
|
595
|
|
- });
|
|
304
|
+ get proxyUri() {
|
|
305
|
+ const { type, address, port, username, password } = this.#settings.proxy;
|
|
306
|
+ switch (type) {
|
|
307
|
+ case TorProxyType.Socks4:
|
|
308
|
+ return `socks4a://${address}:${port}`;
|
|
309
|
+ case TorProxyType.Socks5:
|
|
310
|
+ if (username) {
|
|
311
|
+ return `socks5://${username}:${password}@${address}:${port}`;
|
|
312
|
+ }
|
|
313
|
+ return `socks5://${address}:${port}`;
|
|
314
|
+ case TorProxyType.HTTPS:
|
|
315
|
+ if (username) {
|
|
316
|
+ return `http://${username}:${password}@${address}:${port}`;
|
|
317
|
+ }
|
|
318
|
+ return `http://${address}:${port}`;
|
596
|
319
|
}
|
597
|
|
- // The group object itself should not be writable.
|
598
|
|
- Object.preventExtensions(group);
|
599
|
|
- Object.defineProperty(this, groupname, {
|
600
|
|
- writable: false,
|
601
|
|
- value: group,
|
602
|
|
- });
|
|
320
|
+ return null;
|
603
|
321
|
}
|
604
|
322
|
|
605
|
323
|
/**
|
... |
... |
@@ -614,12 +332,11 @@ class TorSettingsImpl { |
614
|
332
|
* @param {string|integer} val - The value to parse.
|
615
|
333
|
* @param {boolean} trim - Whether a string value can be stripped of
|
616
|
334
|
* whitespace before parsing.
|
617
|
|
- * @param {function} addError - Callback to add error messages to.
|
618
|
335
|
*
|
619
|
336
|
* @return {integer?} - The port number, or null if the given value was not
|
620
|
337
|
* valid.
|
621
|
338
|
*/
|
622
|
|
- #parsePort(val, trim, addError) {
|
|
339
|
+ #parsePort(val, trim) {
|
623
|
340
|
if (typeof val === "string") {
|
624
|
341
|
if (trim) {
|
625
|
342
|
val = val.trim();
|
... |
... |
@@ -628,13 +345,11 @@ class TorSettingsImpl { |
628
|
345
|
if (this.#portRegex.test(val)) {
|
629
|
346
|
val = Number.parseInt(val, 10);
|
630
|
347
|
} else {
|
631
|
|
- addError(`Invalid port string "${val}"`);
|
632
|
|
- return null;
|
|
348
|
+ throw new Error(`Invalid port string "${val}"`);
|
633
|
349
|
}
|
634
|
350
|
}
|
635
|
351
|
if (!Number.isInteger(val) || val < 1 || val > 65535) {
|
636
|
|
- addError(`Port out of range: ${val}`);
|
637
|
|
- return null;
|
|
352
|
+ throw new Error(`Port out of range: ${val}`);
|
638
|
353
|
}
|
639
|
354
|
return val;
|
640
|
355
|
}
|
... |
... |
@@ -659,13 +374,8 @@ class TorSettingsImpl { |
659
|
374
|
* @param {string} pt The pluggable transport to return the lines for
|
660
|
375
|
* @returns {string[]} The bridge lines in random order
|
661
|
376
|
*/
|
662
|
|
- getBuiltinBridges(pt) {
|
663
|
|
- if (!this.#allowUninitialized) {
|
664
|
|
- this.#checkIfInitialized();
|
665
|
|
- }
|
666
|
|
- // Shuffle so that Tor Browser users do not all try the built-in bridges in
|
667
|
|
- // the same order.
|
668
|
|
- return arrayShuffle(this.#builtinBridges[pt] ?? []);
|
|
377
|
+ #getBuiltinBridges(pt) {
|
|
378
|
+ return this.#builtinBridges[pt] ?? [];
|
669
|
379
|
}
|
670
|
380
|
|
671
|
381
|
/**
|
... |
... |
@@ -698,6 +408,13 @@ class TorSettingsImpl { |
698
|
408
|
lazy.logger.debug("Loaded pt_config.json", config);
|
699
|
409
|
this.#recommendedPT = config.recommendedDefault;
|
700
|
410
|
this.#builtinBridges = config.bridges;
|
|
411
|
+ for (const type in this.#builtinBridges) {
|
|
412
|
+ // Shuffle so that Tor Browser users do not all try the built-in bridges
|
|
413
|
+ // in the same order.
|
|
414
|
+ // Only do this once per session. In particular, we don't re-shuffle if
|
|
415
|
+ // changeSettings is called with the same bridges.builtin_type value.
|
|
416
|
+ this.#builtinBridges[type] = arrayShuffle(this.#builtinBridges[type]);
|
|
417
|
+ }
|
701
|
418
|
} catch (e) {
|
702
|
419
|
lazy.logger.error("Could not load the built-in PT config.", e);
|
703
|
420
|
}
|
... |
... |
@@ -716,18 +433,9 @@ class TorSettingsImpl { |
716
|
433
|
lazy.TorLauncherUtil.shouldStartAndOwnTor &&
|
717
|
434
|
Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)
|
718
|
435
|
) {
|
719
|
|
- // Do not want notifications for initially loaded prefs.
|
720
|
|
- this.#freezeNotifications();
|
721
|
|
- try {
|
722
|
|
- this.#allowUninitialized = true;
|
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.
|
726
|
|
- } finally {
|
727
|
|
- this.#allowUninitialized = false;
|
728
|
|
- this.#notificationQueue.clear();
|
729
|
|
- this.#thawNotifications();
|
730
|
|
- }
|
|
436
|
+ this.#loadFromPrefs();
|
|
437
|
+ // We do not pass on the loaded settings to the TorProvider yet. Instead
|
|
438
|
+ // TorProvider will ask for these once it has initialised.
|
731
|
439
|
}
|
732
|
440
|
|
733
|
441
|
Services.obs.addObserver(this, lazy.LoxTopics.UpdateBridges);
|
... |
... |
@@ -746,16 +454,19 @@ class TorSettingsImpl { |
746
|
454
|
observe(subject, topic) {
|
747
|
455
|
switch (topic) {
|
748
|
456
|
case lazy.LoxTopics.UpdateBridges:
|
749
|
|
- if (this.bridges.lox_id) {
|
750
|
|
- // Fetch the newest bridges.
|
751
|
|
- this.bridges.bridge_strings = lazy.Lox.getBridges(
|
752
|
|
- this.bridges.lox_id
|
753
|
|
- );
|
754
|
|
- // No need to save to prefs since bridge_strings is not stored for Lox
|
755
|
|
- // source. But we do pass on the changes to TorProvider.
|
|
457
|
+ if (
|
|
458
|
+ this.#settings.bridges.lox_id &&
|
|
459
|
+ this.#settings.bridges.source === TorBridgeSource.Lox
|
|
460
|
+ ) {
|
|
461
|
+ // Re-trigger the call to lazy.Lox.getBridges.
|
756
|
462
|
// FIXME: This can compete with TorConnect to reach TorProvider.
|
757
|
463
|
// tor-browser#42316
|
758
|
|
- this.#applySettings();
|
|
464
|
+ this.changeSettings({
|
|
465
|
+ bridges: {
|
|
466
|
+ source: TorBridgeSource.Lox,
|
|
467
|
+ lox_id: this.#settings.bridges.lox_id,
|
|
468
|
+ },
|
|
469
|
+ });
|
759
|
470
|
}
|
760
|
471
|
break;
|
761
|
472
|
}
|
... |
... |
@@ -790,23 +501,24 @@ class TorSettingsImpl { |
790
|
501
|
lazy.logger.debug("loadFromPrefs()");
|
791
|
502
|
|
792
|
503
|
/* Quickstart */
|
793
|
|
- this.quickstart.enabled = Services.prefs.getBoolPref(
|
|
504
|
+ this.#settings.quickstart.enabled = Services.prefs.getBoolPref(
|
794
|
505
|
TorSettingsPrefs.quickstart.enabled,
|
795
|
506
|
false
|
796
|
507
|
);
|
797
|
508
|
/* Bridges */
|
798
|
|
- this.bridges.enabled = Services.prefs.getBoolPref(
|
|
509
|
+ const bridges = {};
|
|
510
|
+ bridges.enabled = Services.prefs.getBoolPref(
|
799
|
511
|
TorSettingsPrefs.bridges.enabled,
|
800
|
512
|
false
|
801
|
513
|
);
|
802
|
|
- this.bridges.source = Services.prefs.getIntPref(
|
|
514
|
+ bridges.source = Services.prefs.getIntPref(
|
803
|
515
|
TorSettingsPrefs.bridges.source,
|
804
|
516
|
TorBridgeSource.Invalid
|
805
|
517
|
);
|
806
|
|
- switch (this.bridges.source) {
|
|
518
|
+ switch (bridges.source) {
|
807
|
519
|
case TorBridgeSource.BridgeDB:
|
808
|
520
|
case TorBridgeSource.UserProvided:
|
809
|
|
- this.bridges.bridge_strings = Services.prefs
|
|
521
|
+ bridges.bridge_strings = Services.prefs
|
810
|
522
|
.getBranch(TorSettingsPrefs.bridges.bridge_strings)
|
811
|
523
|
.getChildList("")
|
812
|
524
|
.map(pref =>
|
... |
... |
@@ -817,60 +529,79 @@ class TorSettingsImpl { |
817
|
529
|
break;
|
818
|
530
|
case TorBridgeSource.BuiltIn:
|
819
|
531
|
// bridge_strings is set via builtin_type.
|
820
|
|
- this.bridges.builtin_type = Services.prefs.getStringPref(
|
|
532
|
+ bridges.builtin_type = Services.prefs.getStringPref(
|
821
|
533
|
TorSettingsPrefs.bridges.builtin_type,
|
822
|
534
|
""
|
823
|
535
|
);
|
824
|
536
|
break;
|
825
|
537
|
case TorBridgeSource.Lox:
|
826
|
538
|
// bridge_strings is set via lox id.
|
827
|
|
- this.bridges.lox_id = Services.prefs.getStringPref(
|
|
539
|
+ bridges.lox_id = Services.prefs.getStringPref(
|
828
|
540
|
TorSettingsPrefs.bridges.lox_id,
|
829
|
541
|
""
|
830
|
542
|
);
|
831
|
543
|
break;
|
832
|
544
|
}
|
|
545
|
+ try {
|
|
546
|
+ this.#fixupBridgeSettings(bridges);
|
|
547
|
+ this.#settings.bridges = bridges;
|
|
548
|
+ } catch (error) {
|
|
549
|
+ lazy.logger.error("Loaded bridge preferences failed", error);
|
|
550
|
+ // Keep the default #settings.bridges.
|
|
551
|
+ }
|
|
552
|
+
|
833
|
553
|
/* Proxy */
|
834
|
|
- this.proxy.enabled = Services.prefs.getBoolPref(
|
|
554
|
+ const proxy = {};
|
|
555
|
+ proxy.enabled = Services.prefs.getBoolPref(
|
835
|
556
|
TorSettingsPrefs.proxy.enabled,
|
836
|
557
|
false
|
837
|
558
|
);
|
838
|
|
- if (this.proxy.enabled) {
|
839
|
|
- this.proxy.type = Services.prefs.getIntPref(
|
|
559
|
+ if (proxy.enabled) {
|
|
560
|
+ proxy.type = Services.prefs.getIntPref(
|
840
|
561
|
TorSettingsPrefs.proxy.type,
|
841
|
562
|
TorProxyType.Invalid
|
842
|
563
|
);
|
843
|
|
- this.proxy.address = Services.prefs.getStringPref(
|
|
564
|
+ proxy.address = Services.prefs.getStringPref(
|
844
|
565
|
TorSettingsPrefs.proxy.address,
|
845
|
566
|
""
|
846
|
567
|
);
|
847
|
|
- this.proxy.port = Services.prefs.getIntPref(
|
848
|
|
- TorSettingsPrefs.proxy.port,
|
849
|
|
- 0
|
850
|
|
- );
|
851
|
|
- this.proxy.username = Services.prefs.getStringPref(
|
|
568
|
+ proxy.port = Services.prefs.getIntPref(TorSettingsPrefs.proxy.port, 0);
|
|
569
|
+ proxy.username = Services.prefs.getStringPref(
|
852
|
570
|
TorSettingsPrefs.proxy.username,
|
853
|
571
|
""
|
854
|
572
|
);
|
855
|
|
- this.proxy.password = Services.prefs.getStringPref(
|
|
573
|
+ proxy.password = Services.prefs.getStringPref(
|
856
|
574
|
TorSettingsPrefs.proxy.password,
|
857
|
575
|
""
|
858
|
576
|
);
|
859
|
577
|
}
|
|
578
|
+ try {
|
|
579
|
+ this.#fixupProxySettings(proxy);
|
|
580
|
+ this.#settings.proxy = proxy;
|
|
581
|
+ } catch (error) {
|
|
582
|
+ lazy.logger.error("Loaded proxy preferences failed", error);
|
|
583
|
+ // Keep the default #settings.proxy.
|
|
584
|
+ }
|
860
|
585
|
|
861
|
586
|
/* Firewall */
|
862
|
|
- this.firewall.enabled = Services.prefs.getBoolPref(
|
|
587
|
+ const firewall = {};
|
|
588
|
+ firewall.enabled = Services.prefs.getBoolPref(
|
863
|
589
|
TorSettingsPrefs.firewall.enabled,
|
864
|
590
|
false
|
865
|
591
|
);
|
866
|
|
- if (this.firewall.enabled) {
|
867
|
|
- this.firewall.allowed_ports = Services.prefs.getStringPref(
|
|
592
|
+ if (firewall.enabled) {
|
|
593
|
+ firewall.allowed_ports = Services.prefs.getStringPref(
|
868
|
594
|
TorSettingsPrefs.firewall.allowed_ports,
|
869
|
595
|
""
|
870
|
596
|
);
|
871
|
597
|
}
|
872
|
|
-
|
873
|
|
- this.#cleanupSettings();
|
|
598
|
+ try {
|
|
599
|
+ this.#fixupFirewallSettings(firewall);
|
|
600
|
+ this.#settings.firewall = firewall;
|
|
601
|
+ } catch (error) {
|
|
602
|
+ lazy.logger.error("Loaded firewall preferences failed", error);
|
|
603
|
+ // Keep the default #settings.firewall.
|
|
604
|
+ }
|
874
|
605
|
}
|
875
|
606
|
|
876
|
607
|
/**
|
... |
... |
@@ -880,29 +611,28 @@ class TorSettingsImpl { |
880
|
611
|
lazy.logger.debug("saveToPrefs()");
|
881
|
612
|
|
882
|
613
|
this.#checkIfInitialized();
|
883
|
|
- this.#cleanupSettings();
|
884
|
614
|
|
885
|
615
|
/* Quickstart */
|
886
|
616
|
Services.prefs.setBoolPref(
|
887
|
617
|
TorSettingsPrefs.quickstart.enabled,
|
888
|
|
- this.quickstart.enabled
|
|
618
|
+ this.#settings.quickstart.enabled
|
889
|
619
|
);
|
890
|
620
|
/* Bridges */
|
891
|
621
|
Services.prefs.setBoolPref(
|
892
|
622
|
TorSettingsPrefs.bridges.enabled,
|
893
|
|
- this.bridges.enabled
|
|
623
|
+ this.#settings.bridges.enabled
|
894
|
624
|
);
|
895
|
625
|
Services.prefs.setIntPref(
|
896
|
626
|
TorSettingsPrefs.bridges.source,
|
897
|
|
- this.bridges.source
|
|
627
|
+ this.#settings.bridges.source
|
898
|
628
|
);
|
899
|
629
|
Services.prefs.setStringPref(
|
900
|
630
|
TorSettingsPrefs.bridges.builtin_type,
|
901
|
|
- this.bridges.builtin_type
|
|
631
|
+ this.#settings.bridges.builtin_type
|
902
|
632
|
);
|
903
|
633
|
Services.prefs.setStringPref(
|
904
|
634
|
TorSettingsPrefs.bridges.lox_id,
|
905
|
|
- this.bridges.lox_id
|
|
635
|
+ this.#settings.bridges.lox_id
|
906
|
636
|
);
|
907
|
637
|
// erase existing bridge strings
|
908
|
638
|
const bridgeBranchPrefs = Services.prefs
|
... |
... |
@@ -915,10 +645,10 @@ class TorSettingsImpl { |
915
|
645
|
});
|
916
|
646
|
// write new ones
|
917
|
647
|
if (
|
918
|
|
- this.bridges.source !== TorBridgeSource.Lox &&
|
919
|
|
- this.bridges.source !== TorBridgeSource.BuiltIn
|
|
648
|
+ this.#settings.bridges.source !== TorBridgeSource.Lox &&
|
|
649
|
+ this.#settings.bridges.source !== TorBridgeSource.BuiltIn
|
920
|
650
|
) {
|
921
|
|
- this.bridges.bridge_strings.forEach((string, index) => {
|
|
651
|
+ this.#settings.bridges.bridge_strings.forEach((string, index) => {
|
922
|
652
|
Services.prefs.setStringPref(
|
923
|
653
|
`${TorSettingsPrefs.bridges.bridge_strings}.${index}`,
|
924
|
654
|
string
|
... |
... |
@@ -928,22 +658,28 @@ class TorSettingsImpl { |
928
|
658
|
/* Proxy */
|
929
|
659
|
Services.prefs.setBoolPref(
|
930
|
660
|
TorSettingsPrefs.proxy.enabled,
|
931
|
|
- this.proxy.enabled
|
|
661
|
+ this.#settings.proxy.enabled
|
932
|
662
|
);
|
933
|
|
- if (this.proxy.enabled) {
|
934
|
|
- Services.prefs.setIntPref(TorSettingsPrefs.proxy.type, this.proxy.type);
|
|
663
|
+ if (this.#settings.proxy.enabled) {
|
|
664
|
+ Services.prefs.setIntPref(
|
|
665
|
+ TorSettingsPrefs.proxy.type,
|
|
666
|
+ this.#settings.proxy.type
|
|
667
|
+ );
|
935
|
668
|
Services.prefs.setStringPref(
|
936
|
669
|
TorSettingsPrefs.proxy.address,
|
937
|
|
- this.proxy.address
|
|
670
|
+ this.#settings.proxy.address
|
|
671
|
+ );
|
|
672
|
+ Services.prefs.setIntPref(
|
|
673
|
+ TorSettingsPrefs.proxy.port,
|
|
674
|
+ this.#settings.proxy.port
|
938
|
675
|
);
|
939
|
|
- Services.prefs.setIntPref(TorSettingsPrefs.proxy.port, this.proxy.port);
|
940
|
676
|
Services.prefs.setStringPref(
|
941
|
677
|
TorSettingsPrefs.proxy.username,
|
942
|
|
- this.proxy.username
|
|
678
|
+ this.#settings.proxy.username
|
943
|
679
|
);
|
944
|
680
|
Services.prefs.setStringPref(
|
945
|
681
|
TorSettingsPrefs.proxy.password,
|
946
|
|
- this.proxy.password
|
|
682
|
+ this.#settings.proxy.password
|
947
|
683
|
);
|
948
|
684
|
} else {
|
949
|
685
|
Services.prefs.clearUserPref(TorSettingsPrefs.proxy.type);
|
... |
... |
@@ -955,12 +691,12 @@ class TorSettingsImpl { |
955
|
691
|
/* Firewall */
|
956
|
692
|
Services.prefs.setBoolPref(
|
957
|
693
|
TorSettingsPrefs.firewall.enabled,
|
958
|
|
- this.firewall.enabled
|
|
694
|
+ this.#settings.firewall.enabled
|
959
|
695
|
);
|
960
|
|
- if (this.firewall.enabled) {
|
|
696
|
+ if (this.#settings.firewall.enabled) {
|
961
|
697
|
Services.prefs.setStringPref(
|
962
|
698
|
TorSettingsPrefs.firewall.allowed_ports,
|
963
|
|
- this.firewall.allowed_ports.join(",")
|
|
699
|
+ this.#settings.firewall.allowed_ports.join(",")
|
964
|
700
|
);
|
965
|
701
|
} else {
|
966
|
702
|
Services.prefs.clearUserPref(TorSettingsPrefs.firewall.allowed_ports);
|
... |
... |
@@ -977,11 +713,135 @@ class TorSettingsImpl { |
977
|
713
|
* frontend consumers.
|
978
|
714
|
*/
|
979
|
715
|
async #applySettings() {
|
980
|
|
- this.#checkIfInitialized();
|
981
|
716
|
const provider = await lazy.TorProviderBuilder.build();
|
982
|
717
|
await provider.writeSettings();
|
983
|
718
|
}
|
984
|
719
|
|
|
720
|
+ /**
|
|
721
|
+ * Fixup the given bridges settings to fill in details, establish the correct
|
|
722
|
+ * types and clean up.
|
|
723
|
+ *
|
|
724
|
+ * May throw if there is an error in the given values.
|
|
725
|
+ *
|
|
726
|
+ * @param {Object} bridges - The bridges settings to fix up.
|
|
727
|
+ */
|
|
728
|
+ #fixupBridgeSettings(bridges) {
|
|
729
|
+ if (!Object.values(TorBridgeSource).includes(bridges.source)) {
|
|
730
|
+ throw new Error(`Not a valid bridge source: "${bridges.source}"`);
|
|
731
|
+ }
|
|
732
|
+
|
|
733
|
+ if ("enabled" in bridges) {
|
|
734
|
+ bridges.enabled = Boolean(bridges.enabled);
|
|
735
|
+ }
|
|
736
|
+
|
|
737
|
+ // Set bridge_strings
|
|
738
|
+ switch (bridges.source) {
|
|
739
|
+ case TorBridgeSource.UserProvided:
|
|
740
|
+ case TorBridgeSource.BridgeDB:
|
|
741
|
+ // Only accept an Array for UserProvided and BridgeDB bridge_strings.
|
|
742
|
+ break;
|
|
743
|
+ case TorBridgeSource.BuiltIn:
|
|
744
|
+ bridges.builtin_type = String(bridges.builtin_type);
|
|
745
|
+ bridges.bridge_strings = this.#getBuiltinBridges(bridges.builtin_type);
|
|
746
|
+ break;
|
|
747
|
+ case TorBridgeSource.Lox:
|
|
748
|
+ bridges.lox_id = String(bridges.lox_id);
|
|
749
|
+ bridges.bridge_strings = lazy.Lox.getBridges(bridges.lox_id);
|
|
750
|
+ break;
|
|
751
|
+ case TorBridgeSource.Invalid:
|
|
752
|
+ bridges.bridge_strings = [];
|
|
753
|
+ break;
|
|
754
|
+ }
|
|
755
|
+
|
|
756
|
+ if (
|
|
757
|
+ !Array.isArray(bridges.bridge_strings) ||
|
|
758
|
+ bridges.bridge_strings.some(str => typeof str !== "string")
|
|
759
|
+ ) {
|
|
760
|
+ throw new Error("bridge_strings should be an Array of strings");
|
|
761
|
+ }
|
|
762
|
+
|
|
763
|
+ if (
|
|
764
|
+ bridges.source !== TorBridgeSource.Invalid &&
|
|
765
|
+ !bridges.bridge_strings?.length
|
|
766
|
+ ) {
|
|
767
|
+ throw new Error(
|
|
768
|
+ `Missing bridge_strings for bridge source ${bridges.source}`
|
|
769
|
+ );
|
|
770
|
+ }
|
|
771
|
+
|
|
772
|
+ if (bridges.source !== TorBridgeSource.BuiltIn) {
|
|
773
|
+ bridges.builtin_type = "";
|
|
774
|
+ }
|
|
775
|
+ if (bridges.source !== TorBridgeSource.Lox) {
|
|
776
|
+ bridges.lox_id = "";
|
|
777
|
+ }
|
|
778
|
+
|
|
779
|
+ if (bridges.source === TorBridgeSource.Invalid) {
|
|
780
|
+ bridges.enabled = false;
|
|
781
|
+ }
|
|
782
|
+ }
|
|
783
|
+
|
|
784
|
+ /**
|
|
785
|
+ * Fixup the given proxy settings to fill in details, establish the correct
|
|
786
|
+ * types and clean up.
|
|
787
|
+ *
|
|
788
|
+ * May throw if there is an error in the given values.
|
|
789
|
+ *
|
|
790
|
+ * @param {Object} proxy - The proxy settings to fix up.
|
|
791
|
+ */
|
|
792
|
+ #fixupProxySettings(proxy) {
|
|
793
|
+ proxy.enabled = Boolean(proxy.enabled);
|
|
794
|
+ if (!proxy.enabled) {
|
|
795
|
+ proxy.type = TorProxyType.Invalid;
|
|
796
|
+ proxy.address = "";
|
|
797
|
+ proxy.port = 0;
|
|
798
|
+ proxy.username = "";
|
|
799
|
+ proxy.password = "";
|
|
800
|
+ return;
|
|
801
|
+ }
|
|
802
|
+
|
|
803
|
+ if (!Object.values(TorProxyType).includes(proxy.type)) {
|
|
804
|
+ throw new Error(`Invalid proxy type: ${proxy.type}`);
|
|
805
|
+ }
|
|
806
|
+ proxy.port = this.#parsePort(proxy.port, false);
|
|
807
|
+ proxy.address = String(proxy.address);
|
|
808
|
+ proxy.username = String(proxy.username);
|
|
809
|
+ proxy.password = String(proxy.password);
|
|
810
|
+ }
|
|
811
|
+
|
|
812
|
+ /**
|
|
813
|
+ * Fixup the given firewall settings to fill in details, establish the correct
|
|
814
|
+ * types and clean up.
|
|
815
|
+ *
|
|
816
|
+ * May throw if there is an error in the given values.
|
|
817
|
+ *
|
|
818
|
+ * @param {Object} firewall - The proxy settings to fix up.
|
|
819
|
+ */
|
|
820
|
+ #fixupFirewallSettings(firewall) {
|
|
821
|
+ firewall.enabled = Boolean(firewall.enabled);
|
|
822
|
+ if (!firewall.enabled) {
|
|
823
|
+ firewall.allowed_ports = [];
|
|
824
|
+ return;
|
|
825
|
+ }
|
|
826
|
+
|
|
827
|
+ let allowed_ports = firewall.allowed_ports;
|
|
828
|
+ if (!Array.isArray(allowed_ports)) {
|
|
829
|
+ allowed_ports = allowed_ports === "" ? [] : allowed_ports.split(",");
|
|
830
|
+ }
|
|
831
|
+ // parse and remove duplicates
|
|
832
|
+ const portSet = new Set();
|
|
833
|
+
|
|
834
|
+ for (const port of allowed_ports) {
|
|
835
|
+ try {
|
|
836
|
+ portSet.add(this.#parsePort(port, true));
|
|
837
|
+ } catch (e) {
|
|
838
|
+ // Do not throw for individual ports.
|
|
839
|
+ lazy.logger.error(`Failed to parse the port ${port}. Ignoring.`, e);
|
|
840
|
+ }
|
|
841
|
+ }
|
|
842
|
+ firewall.allowed_ports = [...portSet];
|
|
843
|
+ }
|
|
844
|
+
|
985
|
845
|
/**
|
986
|
846
|
* Change the Tor settings in use.
|
987
|
847
|
*
|
... |
... |
@@ -994,101 +854,128 @@ class TorSettingsImpl { |
994
|
854
|
* + proxy settings can be set as a group.
|
995
|
855
|
* + firewall settings can be set a group.
|
996
|
856
|
*
|
997
|
|
- * @param {object} settings - The settings object to set.
|
|
857
|
+ * @param {object} newValues - The new setting values, a subset of the
|
|
858
|
+ * complete settings that should be changed.
|
998
|
859
|
*/
|
999
|
|
- async changeSettings(settings) {
|
1000
|
|
- lazy.logger.debug("changeSettings()", settings);
|
|
860
|
+ async changeSettings(newValues) {
|
|
861
|
+ lazy.logger.debug("changeSettings()", newValues);
|
1001
|
862
|
this.#checkIfInitialized();
|
1002
|
863
|
|
1003
|
|
- const backup = this.getSettings();
|
1004
|
|
- const backupNotifications = [...this.#notificationQueue];
|
1005
|
|
- // Start collecting errors.
|
1006
|
|
- this.#settingErrors = [];
|
1007
|
|
-
|
1008
|
|
- // Hold off on lots of notifications until all settings are changed.
|
1009
|
|
- this.#freezeNotifications();
|
1010
|
|
- try {
|
1011
|
|
- if ("quickstart" in settings && "enabled" in settings.quickstart) {
|
1012
|
|
- this.quickstart.enabled = !!settings.quickstart.enabled;
|
|
864
|
+ // Make a structured clone since we change the object and may adopt some of
|
|
865
|
+ // the Array values.
|
|
866
|
+ newValues = structuredClone(newValues);
|
|
867
|
+
|
|
868
|
+ const completeSettings = structuredClone(this.#settings);
|
|
869
|
+ const changes = [];
|
|
870
|
+
|
|
871
|
+ /**
|
|
872
|
+ * Change the given setting to a new value. Does nothing if the new value
|
|
873
|
+ * equals the old one, otherwise the change will be recorded in `changes`.
|
|
874
|
+ *
|
|
875
|
+ * @param {string} group - The group name for the property.
|
|
876
|
+ * @param {string} prop - The property name within the group.
|
|
877
|
+ * @param {any} value - The value to set.
|
|
878
|
+ * @param [Function?] equal - A method to test equality between the old and
|
|
879
|
+ * new value. Otherwise uses `===` to check equality.
|
|
880
|
+ */
|
|
881
|
+ const changeSetting = (group, prop, value, equal = null) => {
|
|
882
|
+ const currentValue = this.#settings[group][prop];
|
|
883
|
+ if (equal ? equal(currentValue, value) : currentValue === value) {
|
|
884
|
+ return;
|
1013
|
885
|
}
|
|
886
|
+ completeSettings[group][prop] = value;
|
|
887
|
+ changes.push(`${group}.${prop}`);
|
|
888
|
+ };
|
1014
|
889
|
|
1015
|
|
- if ("bridges" in settings) {
|
1016
|
|
- if ("enabled" in settings.bridges) {
|
1017
|
|
- this.bridges.enabled = !!settings.bridges.enabled;
|
1018
|
|
- }
|
1019
|
|
- if ("source" in settings.bridges) {
|
1020
|
|
- this.bridges.source = settings.bridges.source;
|
1021
|
|
- switch (settings.bridges.source) {
|
1022
|
|
- case TorBridgeSource.BridgeDB:
|
1023
|
|
- case TorBridgeSource.UserProvided:
|
1024
|
|
- this.bridges.bridge_strings = settings.bridges.bridge_strings;
|
1025
|
|
- break;
|
1026
|
|
- case TorBridgeSource.BuiltIn:
|
1027
|
|
- this.bridges.builtin_type = settings.bridges.builtin_type;
|
1028
|
|
- break;
|
1029
|
|
- case TorBridgeSource.Lox:
|
1030
|
|
- this.bridges.lox_id = settings.bridges.lox_id;
|
1031
|
|
- break;
|
1032
|
|
- case TorBridgeSource.Invalid:
|
1033
|
|
- break;
|
1034
|
|
- case undefined:
|
1035
|
|
- break;
|
1036
|
|
- }
|
1037
|
|
- }
|
1038
|
|
- }
|
|
890
|
+ if ("quickstart" in newValues && "enabled" in newValues.quickstart) {
|
|
891
|
+ changeSetting(
|
|
892
|
+ "quickstart",
|
|
893
|
+ "enabled",
|
|
894
|
+ Boolean(newValues.quickstart.enabled)
|
|
895
|
+ );
|
|
896
|
+ }
|
1039
|
897
|
|
1040
|
|
- if ("proxy" in settings) {
|
1041
|
|
- // proxy settings have to be set as a group.
|
1042
|
|
- this.proxy.enabled = !!settings.proxy.enabled;
|
1043
|
|
- if (this.proxy.enabled) {
|
1044
|
|
- this.proxy.type = settings.proxy.type;
|
1045
|
|
- this.proxy.address = settings.proxy.address;
|
1046
|
|
- this.proxy.port = settings.proxy.port;
|
1047
|
|
- this.proxy.username = settings.proxy.username;
|
1048
|
|
- this.proxy.password = settings.proxy.password;
|
|
898
|
+ if ("bridges" in newValues) {
|
|
899
|
+ if ("source" in newValues.bridges) {
|
|
900
|
+ this.#fixupBridgeSettings(newValues.bridges);
|
|
901
|
+ changeSetting("bridges", "source", newValues.bridges.source);
|
|
902
|
+ changeSetting(
|
|
903
|
+ "bridges",
|
|
904
|
+ "bridge_strings",
|
|
905
|
+ newValues.bridges.bridge_strings,
|
|
906
|
+ this.#arrayEqual
|
|
907
|
+ );
|
|
908
|
+ changeSetting("bridges", "lox_id", newValues.bridges.lox_id);
|
|
909
|
+ changeSetting(
|
|
910
|
+ "bridges",
|
|
911
|
+ "builtin_type",
|
|
912
|
+ newValues.bridges.builtin_type
|
|
913
|
+ );
|
|
914
|
+ } else if ("enabled" in newValues.bridges) {
|
|
915
|
+ // Don't need to fixup all the settings, just need to ensure that the
|
|
916
|
+ // enabled value is compatible with the current source.
|
|
917
|
+ newValues.bridges.enabled = Boolean(newValues.bridges.enabled);
|
|
918
|
+ if (
|
|
919
|
+ newValues.bridges.enabled &&
|
|
920
|
+ completeSettings.bridges.source === TorBridgeSource.Invalid
|
|
921
|
+ ) {
|
|
922
|
+ throw new Error("Cannot enable bridges without a bridge source.");
|
1049
|
923
|
}
|
1050
|
924
|
}
|
1051
|
|
-
|
1052
|
|
- if ("firewall" in settings) {
|
1053
|
|
- // firewall settings have to be set as a group.
|
1054
|
|
- this.firewall.enabled = !!settings.firewall.enabled;
|
1055
|
|
- if (this.firewall.enabled) {
|
1056
|
|
- this.firewall.allowed_ports = settings.firewall.allowed_ports;
|
1057
|
|
- }
|
|
925
|
+ if ("enabled" in newValues.bridges) {
|
|
926
|
+ changeSetting("bridges", "enabled", newValues.bridges.enabled);
|
1058
|
927
|
}
|
|
928
|
+ }
|
1059
|
929
|
|
1060
|
|
- this.#cleanupSettings();
|
|
930
|
+ if ("proxy" in newValues) {
|
|
931
|
+ // proxy settings have to be set as a group.
|
|
932
|
+ this.#fixupProxySettings(newValues.proxy);
|
|
933
|
+ changeSetting("proxy", "enabled", Boolean(newValues.proxy.enabled));
|
|
934
|
+ changeSetting("proxy", "type", newValues.proxy.type);
|
|
935
|
+ changeSetting("proxy", "address", newValues.proxy.address);
|
|
936
|
+ changeSetting("proxy", "port", newValues.proxy.port);
|
|
937
|
+ changeSetting("proxy", "username", newValues.proxy.username);
|
|
938
|
+ changeSetting("proxy", "password", newValues.proxy.password);
|
|
939
|
+ }
|
1061
|
940
|
|
1062
|
|
- if (this.#settingErrors.length) {
|
1063
|
|
- throw Error(this.#settingErrors.join("; "));
|
1064
|
|
- }
|
1065
|
|
- this.#saveToPrefs();
|
1066
|
|
- } catch (ex) {
|
1067
|
|
- // Restore the old settings without any new notifications generated from
|
1068
|
|
- // the above code.
|
1069
|
|
- // NOTE: Since the code that changes #settings is not async, it should not
|
1070
|
|
- // be possible for some other call to TorSettings to change anything
|
1071
|
|
- // whilst we are in this context (other than lower down in this call
|
1072
|
|
- // stack), so it is safe to discard all changes to settings and
|
1073
|
|
- // notifications.
|
1074
|
|
- this.#settings = backup;
|
1075
|
|
- this.#notificationQueue.clear();
|
1076
|
|
- for (const notification of backupNotifications) {
|
1077
|
|
- this.#notificationQueue.add(notification);
|
1078
|
|
- }
|
|
941
|
+ if ("firewall" in newValues) {
|
|
942
|
+ // firewall settings have to be set as a group.
|
|
943
|
+ this.#fixupFirewallSettings(newValues.firewall);
|
|
944
|
+ changeSetting("firewall", "enabled", Boolean(newValues.firewall.enabled));
|
|
945
|
+ changeSetting(
|
|
946
|
+ "firewall",
|
|
947
|
+ "allowed_ports",
|
|
948
|
+ newValues.firewall.allowed_ports,
|
|
949
|
+ this.#arrayEqual
|
|
950
|
+ );
|
|
951
|
+ }
|
|
952
|
+
|
|
953
|
+ // No errors so far, so save and commit.
|
|
954
|
+ this.#settings = completeSettings;
|
|
955
|
+ this.#saveToPrefs();
|
1079
|
956
|
|
1080
|
|
- throw ex;
|
1081
|
|
- } finally {
|
1082
|
|
- this.#thawNotifications();
|
1083
|
|
- // Stop collecting errors.
|
1084
|
|
- this.#settingErrors = null;
|
|
957
|
+ if (changes.length) {
|
|
958
|
+ Services.obs.notifyObservers(
|
|
959
|
+ { changes },
|
|
960
|
+ TorSettingsTopics.SettingsChanged
|
|
961
|
+ );
|
1085
|
962
|
}
|
1086
|
963
|
|
1087
|
|
- lazy.logger.debug("setSettings result", this.#settings);
|
|
964
|
+ lazy.logger.debug("setSettings result", this.#settings, changes);
|
1088
|
965
|
|
1089
|
966
|
// After we have sent out the notifications for the changed settings and
|
1090
|
967
|
// saved the preferences we send the new settings to TorProvider.
|
1091
|
|
- await this.#applySettings();
|
|
968
|
+ // Some properties are unread by TorProvider. So if only these values change
|
|
969
|
+ // there is no need to re-apply the settings.
|
|
970
|
+ const unreadProps = [
|
|
971
|
+ "quickstart.enabled",
|
|
972
|
+ "bridges.builtin_type",
|
|
973
|
+ "bridges.lox_id",
|
|
974
|
+ ];
|
|
975
|
+ const shouldApply = changes.some(prop => !unreadProps.includes(prop));
|
|
976
|
+ if (shouldApply) {
|
|
977
|
+ await this.#applySettings();
|
|
978
|
+ }
|
1092
|
979
|
}
|
1093
|
980
|
|
1094
|
981
|
/**
|
... |
... |
@@ -1147,29 +1034,11 @@ class TorSettingsImpl { |
1147
|
1034
|
const bridgeSettings = {
|
1148
|
1035
|
enabled: true,
|
1149
|
1036
|
source: bridges.source,
|
|
1037
|
+ builtin_type: String(bridges.builtin_type),
|
|
1038
|
+ bridge_strings: structuredClone(bridges.bridge_strings),
|
1150
|
1039
|
};
|
1151
|
1040
|
|
1152
|
|
- if (bridges.source === TorBridgeSource.BuiltIn) {
|
1153
|
|
- if (!bridges.builtin_type) {
|
1154
|
|
- throw Error("Missing a built-in type");
|
1155
|
|
- }
|
1156
|
|
- bridgeSettings.builtin_type = String(bridges.builtin_type);
|
1157
|
|
- const bridgeStrings = this.getBuiltinBridges(bridgeSettings.builtin_type);
|
1158
|
|
- if (!bridgeStrings.length) {
|
1159
|
|
- throw new Error(`No builtin bridges for type ${bridges.builtin_type}`);
|
1160
|
|
- }
|
1161
|
|
- bridgeSettings.bridge_strings = bridgeStrings;
|
1162
|
|
- } else {
|
1163
|
|
- // BridgeDB.
|
1164
|
|
- if (!bridges.bridge_strings?.length) {
|
1165
|
|
- throw new Error("Missing bridges strings");
|
1166
|
|
- }
|
1167
|
|
- // TODO: Can we safely verify the format of the bridge addresses sent from
|
1168
|
|
- // Moat?
|
1169
|
|
- bridgeSettings.bridge_strings = Array.from(bridges.bridge_strings, item =>
|
1170
|
|
- String(item)
|
1171
|
|
- );
|
1172
|
|
- }
|
|
1041
|
+ this.#fixupBridgeSettings(bridgeSettings);
|
1173
|
1042
|
|
1174
|
1043
|
// After checks are complete, we commit them.
|
1175
|
1044
|
this.#temporaryBridgeSettings = bridgeSettings;
|