[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-115.5.0esr-13.5-1] 4 commits: fixup! Bug 40597: Implement TorSettings module



Title: GitLab

Pier Angelo Vendrame pushed to branch tor-browser-115.5.0esr-13.5-1 at The Tor Project / Applications / Tor Browser

Commits:

  • 4dd1eadc
    by Henry Wilkes at 2023-12-13T12:26:50+00:00
    fixup! Bug 40597: Implement TorSettings module
    
    Bug 42036: Refactor TorSettings.
    
    We make sure we send out a notification every time a TorSettings setting
    property changes.
    
    We also place all string parsing in one place (the "transform" method).
    
    We also use empty strings "" instead of null. The previous null values
    would have been saved to preferences as an empty string and read back as
    an empty string, so this keeps it consistent.
    
    Enforce the "enum" types to be one of the existing values.
    
    Use structuredClone for copying the _settings object.
    
    Stop using console logging.
    
    Stop exposing the proxy port setting as an array of one number. This
    only worked before because a _javascript_ array with one member converts
    to the same string as the member itself.
    
    Stop resetting the proxy settings when proxy.enabled is set to true
    since all places that call this should set the other settings afterwards
    anyway.
    
    Stop setting firewall.allowed_ports to 0 rather than [].
    
    Fix setting username and password for HTTP proxy.uri. Before it would
    only do so if _proxyUsername was truthy, but this property was never
    set.
    
  • 2f71ea9b
    by Henry Wilkes at 2023-12-13T12:26:51+00:00
    fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
    
    Bug 42036: Adjust for changes in TorSettings.
    
    New API for SettingChanged notification.
    
    Unset the username and password for SOCKS4 proxies explicitly in the
    dialog settings.
    
  • e3a0346c
    by Henry Wilkes at 2023-12-13T12:26:52+00:00
    fixup! Bug 27476: Implement about:torconnect captive portal within Tor Browser
    
    Bug 42036: Adjust for changes in API for TorSettings SettingChanged
    notification.
    
  • 910bb906
    by Henry Wilkes at 2023-12-13T12:26:52+00:00
    fixup! Bug 40597: Implement TorSettings module
    
    Bug 42036: Remove reference to "self" in TorSettings, and re-indent.
    

4 changed files:

Changes:

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -11,7 +11,7 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
    11 11
       "resource://gre/modules/Timer.jsm"
    
    12 12
     );
    
    13 13
     
    
    14
    -const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
    
    14
    +const { TorSettings, TorSettingsTopics, TorBridgeSource } =
    
    15 15
       ChromeUtils.importESModule("resource://gre/modules/TorSettings.sys.mjs");
    
    16 16
     
    
    17 17
     const { TorParsers } = ChromeUtils.importESModule(
    
    ... ... @@ -285,7 +285,7 @@ const gConnectionPane = (function () {
    285 285
             TorSettings.saveToPrefs().applySettings();
    
    286 286
           });
    
    287 287
           this._enableQuickstartCheckbox.checked = TorSettings.quickstart.enabled;
    
    288
    -      Services.obs.addObserver(this, TorSettingsTopics.SettingChanged);
    
    288
    +      Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged);
    
    289 289
     
    
    290 290
           // Bridge setup
    
    291 291
           prefpane.querySelector(selectors.bridges.header).innerText =
    
    ... ... @@ -885,7 +885,7 @@ const gConnectionPane = (function () {
    885 885
     
    
    886 886
         uninit() {
    
    887 887
           // unregister our observer topics
    
    888
    -      Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
    
    888
    +      Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged);
    
    889 889
           Services.obs.removeObserver(this, TorConnectTopics.StateChange);
    
    890 890
           Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
    
    891 891
           Services.obs.removeObserver(this, "intl:app-locales-changed");
    
    ... ... @@ -903,13 +903,10 @@ const gConnectionPane = (function () {
    903 903
         observe(subject, topic, data) {
    
    904 904
           switch (topic) {
    
    905 905
             // triggered when a TorSettings param has changed
    
    906
    -        case TorSettingsTopics.SettingChanged: {
    
    907
    -          const obj = subject?.wrappedJSObject;
    
    908
    -          switch (data) {
    
    909
    -            case TorSettingsData.QuickStartEnabled: {
    
    910
    -              this._enableQuickstartCheckbox.checked = obj.value;
    
    911
    -              break;
    
    912
    -            }
    
    906
    +        case TorSettingsTopics.SettingsChanged: {
    
    907
    +          if (subject.wrappedJSObject.changes.includes("quickstart.enabled")) {
    
    908
    +            this._enableQuickstartCheckbox.checked =
    
    909
    +              TorSettings.quickstart.enabled;
    
    913 910
               }
    
    914 911
               break;
    
    915 912
             }
    

  • browser/components/torpreferences/content/connectionSettingsDialog.mjs
    ... ... @@ -339,6 +339,8 @@ export class ConnectionSettingsDialog {
    339 339
             TorSettings.proxy.type = type;
    
    340 340
             TorSettings.proxy.address = address;
    
    341 341
             TorSettings.proxy.port = port;
    
    342
    +        TorSettings.proxy.username = "";
    
    343
    +        TorSettings.proxy.password = "";
    
    342 344
             break;
    
    343 345
           case TorProxyType.Socks5:
    
    344 346
             TorSettings.proxy.enabled = true;
    

  • toolkit/components/torconnect/TorConnectParent.sys.mjs
    ... ... @@ -12,7 +12,6 @@ import {
    12 12
     import {
    
    13 13
       TorSettings,
    
    14 14
       TorSettingsTopics,
    
    15
    -  TorSettingsData,
    
    16 15
     } from "resource://gre/modules/TorSettings.sys.mjs";
    
    17 16
     
    
    18 17
     const BroadcastTopic = "about-torconnect:broadcast";
    
    ... ... @@ -115,9 +114,11 @@ export class TorConnectParent extends JSWindowActorParent {
    115 114
                 }
    
    116 115
                 break;
    
    117 116
               }
    
    118
    -          case TorSettingsTopics.SettingChanged: {
    
    119
    -            if (aData === TorSettingsData.QuickStartEnabled) {
    
    120
    -              self.state.QuickStartEnabled = obj.value;
    
    117
    +          case TorSettingsTopics.SettingsChanged: {
    
    118
    +            if (
    
    119
    +              aSubject.wrappedJSObject.changes.includes("quickstart.enabled")
    
    120
    +            ) {
    
    121
    +              self.state.QuickStartEnabled = TorSettings.quickstart.enabled;
    
    121 122
                 } else {
    
    122 123
                   // this isn't a setting torconnect cares about
    
    123 124
                   return;
    
    ... ... @@ -141,7 +142,7 @@ export class TorConnectParent extends JSWindowActorParent {
    141 142
         Services.obs.addObserver(this.torConnectObserver, TorSettingsTopics.Ready);
    
    142 143
         Services.obs.addObserver(
    
    143 144
           this.torConnectObserver,
    
    144
    -      TorSettingsTopics.SettingChanged
    
    145
    +      TorSettingsTopics.SettingsChanged
    
    145 146
         );
    
    146 147
     
    
    147 148
         this.userActionObserver = {
    
    ... ... @@ -168,7 +169,7 @@ export class TorConnectParent extends JSWindowActorParent {
    168 169
         );
    
    169 170
         Services.obs.removeObserver(
    
    170 171
           this.torConnectObserver,
    
    171
    -      TorSettingsTopics.SettingChanged
    
    172
    +      TorSettingsTopics.SettingsChanged
    
    172 173
         );
    
    173 174
         Services.obs.removeObserver(this.userActionObserver, BroadcastTopic);
    
    174 175
       }
    

  • toolkit/modules/TorSettings.sys.mjs
    ... ... @@ -10,15 +10,21 @@ ChromeUtils.defineESModuleGetters(lazy, {
    10 10
       TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    11 11
     });
    
    12 12
     
    
    13
    +ChromeUtils.defineLazyGetter(lazy, "logger", () => {
    
    14
    +  let { ConsoleAPI } = ChromeUtils.importESModule(
    
    15
    +    "resource://gre/modules/Console.sys.mjs"
    
    16
    +  );
    
    17
    +  return new ConsoleAPI({
    
    18
    +    maxLogLevel: "warn",
    
    19
    +    maxLogLevelPref: "browser.torsettings.log_level",
    
    20
    +    prefix: "TorSettings",
    
    21
    +  });
    
    22
    +});
    
    23
    +
    
    13 24
     /* TorSettings observer topics */
    
    14 25
     export const TorSettingsTopics = Object.freeze({
    
    15 26
       Ready: "torsettings:ready",
    
    16
    -  SettingChanged: "torsettings:setting-changed",
    
    17
    -});
    
    18
    -
    
    19
    -/* TorSettings observer data (for SettingChanged topic) */
    
    20
    -export const TorSettingsData = Object.freeze({
    
    21
    -  QuickStartEnabled: "torsettings:quickstart_enabled",
    
    27
    +  SettingsChanged: "torsettings:settings-changed",
    
    22 28
     });
    
    23 29
     
    
    24 30
     /* Prefs used to store settings in TorBrowser prefs */
    
    ... ... @@ -32,7 +38,7 @@ const TorSettingsPrefs = Object.freeze({
    32 38
       bridges: {
    
    33 39
         /* bool:  does tor use bridges */
    
    34 40
         enabled: "torbrowser.settings.bridges.enabled",
    
    35
    -    /* int: -1=invalid|0=builtin|1=bridge_db|2=user_provided */
    
    41
    +    /* int: See TorBridgeSource */
    
    36 42
         source: "torbrowser.settings.bridges.source",
    
    37 43
         /* string: obfs4|meek_azure|snowflake|etc */
    
    38 44
         builtin_type: "torbrowser.settings.bridges.builtin_type",
    
    ... ... @@ -42,7 +48,7 @@ const TorSettingsPrefs = Object.freeze({
    42 48
       proxy: {
    
    43 49
         /* bool: does tor use a proxy */
    
    44 50
         enabled: "torbrowser.settings.proxy.enabled",
    
    45
    -    /* -1=invalid|0=socks4,1=socks5,2=https */
    
    51
    +    /* See TorProxyType */
    
    46 52
         type: "torbrowser.settings.proxy.type",
    
    47 53
         /* string: proxy server address */
    
    48 54
         address: "torbrowser.settings.proxy.address",
    
    ... ... @@ -140,23 +146,6 @@ export const TorBuiltinBridgeTypes = Object.freeze(
    140 146
     
    
    141 147
     /* Parsing Methods */
    
    142 148
     
    
    143
    -// expects a string representation of an integer from 1 to 65535
    
    144
    -const parsePort = function (aPort) {
    
    145
    -  // ensure port string is a valid positive integer
    
    146
    -  const validIntRegex = /^[0-9]+$/;
    
    147
    -  if (!validIntRegex.test(aPort)) {
    
    148
    -    return 0;
    
    149
    -  }
    
    150
    -
    
    151
    -  // ensure port value is on valid range
    
    152
    -  const port = Number.parseInt(aPort);
    
    153
    -  if (port < 1 || port > 65535) {
    
    154
    -    return 0;
    
    155
    -  }
    
    156
    -
    
    157
    -  return port;
    
    158
    -};
    
    159
    -
    
    160 149
     // expects a '\n' or '\r\n' delimited bridge string, which we split and trim
    
    161 150
     // each bridge string can also optionally have 'bridge' at the beginning ie:
    
    162 151
     // bridge $(type) $(address):$(port) $(certificate)
    
    ... ... @@ -176,17 +165,6 @@ const parseBridgeStrings = function (aBridgeStrings) {
    176 165
         .filter(bridgeString => bridgeString != "");
    
    177 166
     };
    
    178 167
     
    
    179
    -// expecting a ',' delimited list of ints with possible white space between
    
    180
    -// returns an array of ints
    
    181
    -const parsePortList = function (aPortListString) {
    
    182
    -  const splitStrings = aPortListString.split(",");
    
    183
    -  // parse and remove duplicates
    
    184
    -  const portSet = new Set(splitStrings.map(val => parsePort(val.trim())));
    
    185
    -  // parsePort returns 0 for failed parses, so remove 0 from list
    
    186
    -  portSet.delete(0);
    
    187
    -  return Array.from(portSet);
    
    188
    -};
    
    189
    -
    
    190 168
     const getBuiltinBridgeStrings = function (builtinType) {
    
    191 169
       if (!builtinType) {
    
    192 170
         return [];
    
    ... ... @@ -235,558 +213,679 @@ const arrayShuffle = function (array) {
    235 213
       }
    
    236 214
     };
    
    237 215
     
    
    238
    -const arrayCopy = function (array) {
    
    239
    -  return [].concat(array);
    
    240
    -};
    
    241
    -
    
    242 216
     /* TorSettings module */
    
    243 217
     
    
    244
    -export const TorSettings = (() => {
    
    245
    -  const self = {
    
    246
    -    _settings: null,
    
    218
    +export const TorSettings = {
    
    219
    +  /**
    
    220
    +   * The underlying settings values.
    
    221
    +   *
    
    222
    +   * @type {object}
    
    223
    +   */
    
    224
    +  _settings: {
    
    225
    +    quickstart: {
    
    226
    +      enabled: false,
    
    227
    +    },
    
    228
    +    bridges: {
    
    229
    +      enabled: false,
    
    230
    +      source: TorBridgeSource.Invalid,
    
    231
    +      builtin_type: "",
    
    232
    +      bridge_strings: [],
    
    233
    +    },
    
    234
    +    proxy: {
    
    235
    +      enabled: false,
    
    236
    +      type: TorProxyType.Invalid,
    
    237
    +      address: "",
    
    238
    +      port: 0,
    
    239
    +      username: "",
    
    240
    +      password: "",
    
    241
    +    },
    
    242
    +    firewall: {
    
    243
    +      enabled: false,
    
    244
    +      allowed_ports: [],
    
    245
    +    },
    
    246
    +  },
    
    247
    +
    
    248
    +  /**
    
    249
    +   * The current number of freezes applied to the notifications.
    
    250
    +   *
    
    251
    +   * @type {integer}
    
    252
    +   */
    
    253
    +  _freezeNotificationsCount: 0,
    
    254
    +  /**
    
    255
    +   * The queue for settings that have changed. To be broadcast in the
    
    256
    +   * notification when not frozen.
    
    257
    +   *
    
    258
    +   * @type {Set<string>}
    
    259
    +   */
    
    260
    +  _notificationQueue: new Set(),
    
    261
    +  /**
    
    262
    +   * Send a notification if we have any queued and we are not frozen.
    
    263
    +   */
    
    264
    +  _tryNotification() {
    
    265
    +    if (this._freezeNotificationsCount || !this._notificationQueue.size) {
    
    266
    +      return;
    
    267
    +    }
    
    268
    +    Services.obs.notifyObservers(
    
    269
    +      { changes: [...this._notificationQueue] },
    
    270
    +      TorSettingsTopics.SettingsChanged
    
    271
    +    );
    
    272
    +    this._notificationQueue.clear();
    
    273
    +  },
    
    274
    +  /**
    
    275
    +   * Pause notifications for changes in setting values. This is useful if you
    
    276
    +   * need to make batch changes to settings.
    
    277
    +   *
    
    278
    +   * This should always be paired with a call to thawNotifications once
    
    279
    +   * notifications should be released. Usually you should wrap whatever
    
    280
    +   * changes you make with a `try` block and call thawNotifications in the
    
    281
    +   * `finally` block.
    
    282
    +   */
    
    283
    +  freezeNotifications() {
    
    284
    +    this._freezeNotificationsCount++;
    
    285
    +  },
    
    286
    +  /**
    
    287
    +   * Release the hold on notifications so they may be sent out.
    
    288
    +   *
    
    289
    +   * Note, if some other method has also frozen the notifications, this will
    
    290
    +   * only release them once it has also called this method.
    
    291
    +   */
    
    292
    +  thawNotifications() {
    
    293
    +    this._freezeNotificationsCount--;
    
    294
    +    this._tryNotification();
    
    295
    +  },
    
    296
    +  /**
    
    297
    +   * @typedef {object} TorSettingProperty
    
    298
    +   *
    
    299
    +   * @property {function} [getter] - A getter for the property. If this is
    
    300
    +   *   given, the property cannot be set.
    
    301
    +   * @property {function} [transform] - Called in the setter for the property,
    
    302
    +   *   with the new value given. Should transform the given value into the
    
    303
    +   *   right type.
    
    304
    +   * @property {function} [equal] - Test whether two values for the property
    
    305
    +   *   are considered equal. Otherwise uses `===`.
    
    306
    +   * @property {function} [callback] - Called whenever the property value
    
    307
    +   *   changes, with the new value given. Should be used to trigger any other
    
    308
    +   *   required changes for the new value.
    
    309
    +   * @property {function} [copy] - Called whenever the property is read, with
    
    310
    +   *   the stored value given. Should return a copy of the value. Otherwise
    
    311
    +   *   returns the stored value.
    
    312
    +   */
    
    313
    +  /**
    
    314
    +   * Add properties to the TorSettings instance, to be read or set.
    
    315
    +   *
    
    316
    +   * @param {string} groupname - The name of the setting group. The given
    
    317
    +   *   settings will be accessible from the TorSettings property of the same
    
    318
    +   *   name.
    
    319
    +   * @param {object<string, TorSettingProperty>} propParams - An object that
    
    320
    +   *   defines the settings to add to this group. The object property names
    
    321
    +   *   will be mapped to properties of TorSettings under the given groupname
    
    322
    +   *   property. Details about the setting should be described in the
    
    323
    +   *   TorSettingProperty property value.
    
    324
    +   */
    
    325
    +  _addProperties(groupname, propParams) {
    
    326
    +    // Create a new object to hold all these settings.
    
    327
    +    const group = {};
    
    328
    +    for (const name in propParams) {
    
    329
    +      const { getter, transform, callback, copy, equal } = propParams[name];
    
    330
    +      Object.defineProperty(group, name, {
    
    331
    +        get: getter
    
    332
    +          ? getter
    
    333
    +          : () => {
    
    334
    +              let val = this._settings[groupname][name];
    
    335
    +              if (copy) {
    
    336
    +                val = copy(val);
    
    337
    +              }
    
    338
    +              // Assume string or number value.
    
    339
    +              return val;
    
    340
    +            },
    
    341
    +        set: getter
    
    342
    +          ? undefined
    
    343
    +          : val => {
    
    344
    +              const prevVal = this._settings[groupname][name];
    
    345
    +              this.freezeNotifications();
    
    346
    +              try {
    
    347
    +                if (transform) {
    
    348
    +                  val = transform(val);
    
    349
    +                }
    
    350
    +                const isEqual = equal ? equal(val, prevVal) : val === prevVal;
    
    351
    +                if (!isEqual) {
    
    352
    +                  if (callback) {
    
    353
    +                    callback(val);
    
    354
    +                  }
    
    355
    +                  this._settings[groupname][name] = val;
    
    356
    +                  this._notificationQueue.add(`${groupname}.${name}`);
    
    357
    +                }
    
    358
    +              } finally {
    
    359
    +                this.thawNotifications();
    
    360
    +              }
    
    361
    +            },
    
    362
    +      });
    
    363
    +    }
    
    364
    +    // The group object itself should not be writable.
    
    365
    +    Object.preventExtensions(group);
    
    366
    +    Object.defineProperty(this, groupname, {
    
    367
    +      writable: false,
    
    368
    +      value: group,
    
    369
    +    });
    
    370
    +  },
    
    247 371
     
    
    248
    -    // tor daemon related settings
    
    249
    -    defaultSettings() {
    
    250
    -      const settings = {
    
    251
    -        quickstart: {
    
    252
    -          enabled: false,
    
    372
    +  /**
    
    373
    +   * Regular _expression_ for a decimal non-negative integer.
    
    374
    +   *
    
    375
    +   * @type {RegExp}
    
    376
    +   */
    
    377
    +  _portRegex: /^[0-9]+$/,
    
    378
    +  /**
    
    379
    +   * Parse a string as a port number.
    
    380
    +   *
    
    381
    +   * @param {string|integer} val - The value to parse.
    
    382
    +   * @param {boolean} trim - Whether a string value can be stripped of
    
    383
    +   *   whitespace before parsing.
    
    384
    +   *
    
    385
    +   * @return {integer?} - The port number, or null if the given value was not
    
    386
    +   *   valid.
    
    387
    +   */
    
    388
    +  _parsePort(val, trim) {
    
    389
    +    if (typeof val === "string") {
    
    390
    +      if (trim) {
    
    391
    +        val = val.trim();
    
    392
    +      }
    
    393
    +      // ensure port string is a valid positive integer
    
    394
    +      if (this._portRegex.test(val)) {
    
    395
    +        val = Number.parseInt(val, 10);
    
    396
    +      } else {
    
    397
    +        lazy.logger.error(`Invalid port string "${val}"`);
    
    398
    +        return null;
    
    399
    +      }
    
    400
    +    }
    
    401
    +    if (!Number.isInteger(val) || val < 1 || val > 65535) {
    
    402
    +      lazy.logger.error(`Port out of range: ${val}`);
    
    403
    +      return null;
    
    404
    +    }
    
    405
    +    return val;
    
    406
    +  },
    
    407
    +  /**
    
    408
    +   * Test whether two arrays have equal members and order.
    
    409
    +   *
    
    410
    +   * @param {Array} val1 - The first array to test.
    
    411
    +   * @param {Array} val2 - The second array to compare against.
    
    412
    +   *
    
    413
    +   * @return {boolean} - Whether the two arrays are equal.
    
    414
    +   */
    
    415
    +  _arrayEqual(val1, val2) {
    
    416
    +    if (val1.length !== val2.length) {
    
    417
    +      return false;
    
    418
    +    }
    
    419
    +    return val1.every((v, i) => v === val2[i]);
    
    420
    +  },
    
    421
    +
    
    422
    +  /* load or init our settings, and register observers */
    
    423
    +  async init() {
    
    424
    +    this._addProperties("quickstart", {
    
    425
    +      enabled: {},
    
    426
    +    });
    
    427
    +    this._addProperties("bridges", {
    
    428
    +      enabled: {},
    
    429
    +      source: {
    
    430
    +        transform: val => {
    
    431
    +          if (Object.values(TorBridgeSource).includes(val)) {
    
    432
    +            return val;
    
    433
    +          }
    
    434
    +          lazy.logger.error(`Not a valid bridge source: "${val}"`);
    
    435
    +          return TorBridgeSource.Invalid;
    
    436
    +        },
    
    437
    +      },
    
    438
    +      bridge_strings: {
    
    439
    +        transform: val => {
    
    440
    +          if (Array.isArray(val)) {
    
    441
    +            return [...val];
    
    442
    +          }
    
    443
    +          return parseBridgeStrings(val);
    
    444
    +        },
    
    445
    +        copy: val => [...val],
    
    446
    +        equal: (val1, val2) => this._arrayEqual(val1, val2),
    
    447
    +      },
    
    448
    +      builtin_type: {
    
    449
    +        callback: val => {
    
    450
    +          if (!val) {
    
    451
    +            // Make sure that the source is not BuiltIn
    
    452
    +            if (this.bridges.source === TorBridgeSource.BuiltIn) {
    
    453
    +              this.bridges.source = TorBridgeSource.Invalid;
    
    454
    +            }
    
    455
    +            return;
    
    456
    +          }
    
    457
    +          const bridgeStrings = getBuiltinBridgeStrings(val);
    
    458
    +          if (bridgeStrings.length) {
    
    459
    +            this.bridges.bridge_strings = bridgeStrings;
    
    460
    +            return;
    
    461
    +          }
    
    462
    +          lazy.logger.error(`No built-in ${val} bridges found`);
    
    463
    +          // Change to be empty, this will trigger this callback again,
    
    464
    +          // but with val as "".
    
    465
    +          this.bridges.builtin_type == "";
    
    253 466
             },
    
    254
    -        bridges: {
    
    255
    -          enabled: false,
    
    256
    -          source: TorBridgeSource.Invalid,
    
    257
    -          builtin_type: null,
    
    258
    -          bridge_strings: [],
    
    467
    +      },
    
    468
    +    });
    
    469
    +    this._addProperties("proxy", {
    
    470
    +      enabled: {
    
    471
    +        callback: val => {
    
    472
    +          if (val) {
    
    473
    +            return;
    
    474
    +          }
    
    475
    +          // Reset proxy settings.
    
    476
    +          this.proxy.type = TorProxyType.Invalid;
    
    477
    +          this.proxy.address = "";
    
    478
    +          this.proxy.port = 0;
    
    479
    +          this.proxy.username = "";
    
    480
    +          this.proxy.password = "";
    
    259 481
             },
    
    260
    -        proxy: {
    
    261
    -          enabled: false,
    
    262
    -          type: TorProxyType.Invalid,
    
    263
    -          address: null,
    
    264
    -          port: 0,
    
    265
    -          username: null,
    
    266
    -          password: null,
    
    482
    +      },
    
    483
    +      type: {
    
    484
    +        transform: val => {
    
    485
    +          if (Object.values(TorProxyType).includes(val)) {
    
    486
    +            return val;
    
    487
    +          }
    
    488
    +          lazy.logger.error(`Not a valid proxy type: "${val}"`);
    
    489
    +          return TorProxyType.Invalid;
    
    267 490
             },
    
    268
    -        firewall: {
    
    269
    -          enabled: false,
    
    270
    -          allowed_ports: [],
    
    491
    +      },
    
    492
    +      address: {},
    
    493
    +      port: {
    
    494
    +        transform: val => {
    
    495
    +          if (val === 0) {
    
    496
    +            // This is a valid value that "unsets" the port.
    
    497
    +            // Keep this value without giving a warning.
    
    498
    +            // NOTE: In contrast, "0" is not valid.
    
    499
    +            return 0;
    
    500
    +          }
    
    501
    +          // Unset to 0 if invalid null is returned.
    
    502
    +          return this._parsePort(val, false) ?? 0;
    
    271 503
             },
    
    272
    -      };
    
    273
    -      return settings;
    
    274
    -    },
    
    275
    -
    
    276
    -    /* load or init our settings, and register observers */
    
    277
    -    async init() {
    
    278
    -      // TODO: We could use a shared promise, and wait for it to be fullfilled
    
    279
    -      // instead of Service.obs.
    
    280
    -      if (lazy.TorLauncherUtil.shouldStartAndOwnTor) {
    
    281
    -        // if the settings branch exists, load settings from prefs
    
    282
    -        if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
    
    504
    +      },
    
    505
    +      username: {},
    
    506
    +      password: {},
    
    507
    +      uri: {
    
    508
    +        getter: () => {
    
    509
    +          const { type, address, port, username, password } = this.proxy;
    
    510
    +          switch (type) {
    
    511
    +            case TorProxyType.Socks4:
    
    512
    +              return `socks4a://${address}:${port}`;
    
    513
    +            case TorProxyType.Socks5:
    
    514
    +              if (username) {
    
    515
    +                return `socks5://${username}:${password}@${address}:${port}`;
    
    516
    +              }
    
    517
    +              return `socks5://${address}:${port}`;
    
    518
    +            case TorProxyType.HTTPS:
    
    519
    +              if (username) {
    
    520
    +                return `http://${username}:${password}@${address}:${port}`;
    
    521
    +              }
    
    522
    +              return `http://${address}:${port}`;
    
    523
    +          }
    
    524
    +          return null;
    
    525
    +        },
    
    526
    +      },
    
    527
    +    });
    
    528
    +    this._addProperties("firewall", {
    
    529
    +      enabled: {
    
    530
    +        callback: val => {
    
    531
    +          if (!val) {
    
    532
    +            this.firewall.allowed_ports = "";
    
    533
    +          }
    
    534
    +        },
    
    535
    +      },
    
    536
    +      allowed_ports: {
    
    537
    +        transform: val => {
    
    538
    +          if (!Array.isArray(val)) {
    
    539
    +            val = val === "" ? [] : val.split(",");
    
    540
    +          }
    
    541
    +          // parse and remove duplicates
    
    542
    +          const portSet = new Set(val.map(p => this._parsePort(p, true)));
    
    543
    +          // parsePort returns null for failed parses, so remove it.
    
    544
    +          portSet.delete(null);
    
    545
    +          return [...portSet];
    
    546
    +        },
    
    547
    +        copy: val => [...val],
    
    548
    +        equal: (val1, val2) => this._arrayEqual(val1, val2),
    
    549
    +      },
    
    550
    +    });
    
    551
    +
    
    552
    +    // TODO: We could use a shared promise, and wait for it to be fullfilled
    
    553
    +    // instead of Service.obs.
    
    554
    +    if (lazy.TorLauncherUtil.shouldStartAndOwnTor) {
    
    555
    +      // if the settings branch exists, load settings from prefs
    
    556
    +      if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
    
    557
    +        // Do not want notifications for initially loaded prefs.
    
    558
    +        this.freezeNotifications();
    
    559
    +        try {
    
    283 560
               this.loadFromPrefs();
    
    284
    -        } else {
    
    285
    -          // otherwise load defaults
    
    286
    -          this._settings = this.defaultSettings();
    
    561
    +        } finally {
    
    562
    +          this._notificationQueue.clear();
    
    563
    +          this.thawNotifications();
    
    287 564
             }
    
    288
    -        Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
    
    289
    -
    
    290
    -        try {
    
    291
    -          const provider = await lazy.TorProviderBuilder.build();
    
    292
    -          if (provider.isRunning) {
    
    293
    -            this.handleProcessReady();
    
    294
    -          }
    
    295
    -        } catch {}
    
    296 565
           }
    
    297
    -    },
    
    566
    +      try {
    
    567
    +        const provider = await lazy.TorProviderBuilder.build();
    
    568
    +        if (provider.isRunning) {
    
    569
    +          this.handleProcessReady();
    
    570
    +          // No need to add an observer to call this again.
    
    571
    +          return;
    
    572
    +        }
    
    573
    +      } catch {}
    
    298 574
     
    
    299
    -    /* wait for relevant life-cycle events to apply saved settings */
    
    300
    -    async observe(subject, topic, data) {
    
    301
    -      console.log(`TorSettings: Observed ${topic}`);
    
    575
    +      Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
    
    576
    +    }
    
    577
    +  },
    
    302 578
     
    
    303
    -      switch (topic) {
    
    304
    -        case lazy.TorProviderTopics.ProcessIsReady:
    
    305
    -          Services.obs.removeObserver(
    
    306
    -            this,
    
    307
    -            lazy.TorProviderTopics.ProcessIsReady
    
    308
    -          );
    
    309
    -          await this.handleProcessReady();
    
    310
    -          break;
    
    311
    -      }
    
    312
    -    },
    
    579
    +  /* wait for relevant life-cycle events to apply saved settings */
    
    580
    +  async observe(subject, topic, data) {
    
    581
    +    lazy.logger.debug(`Observed ${topic}`);
    
    313 582
     
    
    314
    -    // once the tor daemon is ready, we need to apply our settings
    
    315
    -    async handleProcessReady() {
    
    316
    -      // push down settings to tor
    
    317
    -      await this.applySettings();
    
    318
    -      console.log("TorSettings: Ready");
    
    319
    -      Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
    
    320
    -    },
    
    583
    +    switch (topic) {
    
    584
    +      case lazy.TorProviderTopics.ProcessIsReady:
    
    585
    +        Services.obs.removeObserver(
    
    586
    +          this,
    
    587
    +          lazy.TorProviderTopics.ProcessIsReady
    
    588
    +        );
    
    589
    +        await this.handleProcessReady();
    
    590
    +        break;
    
    591
    +    }
    
    592
    +  },
    
    321 593
     
    
    322
    -    // load our settings from prefs
    
    323
    -    loadFromPrefs() {
    
    324
    -      console.log("TorSettings: loadFromPrefs()");
    
    594
    +  // once the tor daemon is ready, we need to apply our settings
    
    595
    +  async handleProcessReady() {
    
    596
    +    // push down settings to tor
    
    597
    +    await this.applySettings();
    
    598
    +    lazy.logger.info("Ready");
    
    599
    +    Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
    
    600
    +  },
    
    325 601
     
    
    326
    -      const settings = this.defaultSettings();
    
    602
    +  // load our settings from prefs
    
    603
    +  loadFromPrefs() {
    
    604
    +    lazy.logger.debug("loadFromPrefs()");
    
    327 605
     
    
    328
    -      /* Quickstart */
    
    329
    -      settings.quickstart.enabled = Services.prefs.getBoolPref(
    
    330
    -        TorSettingsPrefs.quickstart.enabled,
    
    331
    -        false
    
    606
    +    /* Quickstart */
    
    607
    +    this.quickstart.enabled = Services.prefs.getBoolPref(
    
    608
    +      TorSettingsPrefs.quickstart.enabled,
    
    609
    +      false
    
    610
    +    );
    
    611
    +    /* Bridges */
    
    612
    +    this.bridges.enabled = Services.prefs.getBoolPref(
    
    613
    +      TorSettingsPrefs.bridges.enabled,
    
    614
    +      false
    
    615
    +    );
    
    616
    +    this.bridges.source = Services.prefs.getIntPref(
    
    617
    +      TorSettingsPrefs.bridges.source,
    
    618
    +      TorBridgeSource.Invalid
    
    619
    +    );
    
    620
    +    if (this.bridges.source == TorBridgeSource.BuiltIn) {
    
    621
    +      this.bridges.builtin_type = Services.prefs.getStringPref(
    
    622
    +        TorSettingsPrefs.bridges.builtin_type,
    
    623
    +        ""
    
    332 624
           );
    
    333
    -      /* Bridges */
    
    334
    -      settings.bridges.enabled = Services.prefs.getBoolPref(
    
    335
    -        TorSettingsPrefs.bridges.enabled,
    
    336
    -        false
    
    625
    +    } else {
    
    626
    +      const bridgeBranchPrefs = Services.prefs
    
    627
    +        .getBranch(TorSettingsPrefs.bridges.bridge_strings)
    
    628
    +        .getChildList("");
    
    629
    +      this.bridges.bridge_strings = Array.from(bridgeBranchPrefs, pref =>
    
    630
    +        Services.prefs.getStringPref(
    
    631
    +          `${TorSettingsPrefs.bridges.bridge_strings}${pref}`
    
    632
    +        )
    
    337 633
           );
    
    338
    -      settings.bridges.source = Services.prefs.getIntPref(
    
    339
    -        TorSettingsPrefs.bridges.source,
    
    340
    -        TorBridgeSource.Invalid
    
    634
    +    }
    
    635
    +    /* Proxy */
    
    636
    +    this.proxy.enabled = Services.prefs.getBoolPref(
    
    637
    +      TorSettingsPrefs.proxy.enabled,
    
    638
    +      false
    
    639
    +    );
    
    640
    +    if (this.proxy.enabled) {
    
    641
    +      this.proxy.type = Services.prefs.getIntPref(
    
    642
    +        TorSettingsPrefs.proxy.type,
    
    643
    +        TorProxyType.Invalid
    
    341 644
           );
    
    342
    -      if (settings.bridges.source == TorBridgeSource.BuiltIn) {
    
    343
    -        const builtinType = Services.prefs.getStringPref(
    
    344
    -          TorSettingsPrefs.bridges.builtin_type,
    
    345
    -          ""
    
    346
    -        );
    
    347
    -        settings.bridges.builtin_type = builtinType;
    
    348
    -        settings.bridges.bridge_strings = getBuiltinBridgeStrings(builtinType);
    
    349
    -        if (!settings.bridges.bridge_strings.length) {
    
    350
    -          // in this case the user is using a builtin bridge that is no longer supported,
    
    351
    -          // reset to settings to default values
    
    352
    -          console.warn(
    
    353
    -            `[TorSettings] Cannot find any bridge line for the configured bridge type ${builtinType}`
    
    354
    -          );
    
    355
    -          settings.bridges.source = TorBridgeSource.Invalid;
    
    356
    -          settings.bridges.builtin_type = null;
    
    357
    -        }
    
    358
    -      } else {
    
    359
    -        settings.bridges.bridge_strings = [];
    
    360
    -        const bridgeBranchPrefs = Services.prefs
    
    361
    -          .getBranch(TorSettingsPrefs.bridges.bridge_strings)
    
    362
    -          .getChildList("");
    
    363
    -        bridgeBranchPrefs.forEach(pref => {
    
    364
    -          const bridgeString = Services.prefs.getStringPref(
    
    365
    -            `${TorSettingsPrefs.bridges.bridge_strings}${pref}`
    
    366
    -          );
    
    367
    -          settings.bridges.bridge_strings.push(bridgeString);
    
    368
    -        });
    
    369
    -      }
    
    370
    -      /* Proxy */
    
    371
    -      settings.proxy.enabled = Services.prefs.getBoolPref(
    
    372
    -        TorSettingsPrefs.proxy.enabled,
    
    373
    -        false
    
    645
    +      this.proxy.address = Services.prefs.getStringPref(
    
    646
    +        TorSettingsPrefs.proxy.address,
    
    647
    +        ""
    
    374 648
           );
    
    375
    -      if (settings.proxy.enabled) {
    
    376
    -        settings.proxy.type = Services.prefs.getIntPref(
    
    377
    -          TorSettingsPrefs.proxy.type,
    
    378
    -          TorProxyType.Invalid
    
    379
    -        );
    
    380
    -        settings.proxy.address = Services.prefs.getStringPref(
    
    381
    -          TorSettingsPrefs.proxy.address,
    
    382
    -          ""
    
    383
    -        );
    
    384
    -        settings.proxy.port = Services.prefs.getIntPref(
    
    385
    -          TorSettingsPrefs.proxy.port,
    
    386
    -          0
    
    387
    -        );
    
    388
    -        settings.proxy.username = Services.prefs.getStringPref(
    
    389
    -          TorSettingsPrefs.proxy.username,
    
    390
    -          ""
    
    391
    -        );
    
    392
    -        settings.proxy.password = Services.prefs.getStringPref(
    
    393
    -          TorSettingsPrefs.proxy.password,
    
    394
    -          ""
    
    395
    -        );
    
    396
    -      } else {
    
    397
    -        settings.proxy.type = TorProxyType.Invalid;
    
    398
    -        settings.proxy.address = null;
    
    399
    -        settings.proxy.port = 0;
    
    400
    -        settings.proxy.username = null;
    
    401
    -        settings.proxy.password = null;
    
    402
    -      }
    
    403
    -
    
    404
    -      /* Firewall */
    
    405
    -      settings.firewall.enabled = Services.prefs.getBoolPref(
    
    406
    -        TorSettingsPrefs.firewall.enabled,
    
    407
    -        false
    
    649
    +      this.proxy.port = Services.prefs.getIntPref(
    
    650
    +        TorSettingsPrefs.proxy.port,
    
    651
    +        0
    
    408 652
           );
    
    409
    -      if (settings.firewall.enabled) {
    
    410
    -        const portList = Services.prefs.getStringPref(
    
    411
    -          TorSettingsPrefs.firewall.allowed_ports,
    
    412
    -          ""
    
    413
    -        );
    
    414
    -        settings.firewall.allowed_ports = parsePortList(portList);
    
    415
    -      } else {
    
    416
    -        settings.firewall.allowed_ports = 0;
    
    417
    -      }
    
    418
    -
    
    419
    -      this._settings = settings;
    
    420
    -
    
    421
    -      return this;
    
    422
    -    },
    
    653
    +      this.proxy.username = Services.prefs.getStringPref(
    
    654
    +        TorSettingsPrefs.proxy.username,
    
    655
    +        ""
    
    656
    +      );
    
    657
    +      this.proxy.password = Services.prefs.getStringPref(
    
    658
    +        TorSettingsPrefs.proxy.password,
    
    659
    +        ""
    
    660
    +      );
    
    661
    +    }
    
    423 662
     
    
    424
    -    // save our settings to prefs
    
    425
    -    saveToPrefs() {
    
    426
    -      console.log("TorSettings: saveToPrefs()");
    
    663
    +    /* Firewall */
    
    664
    +    this.firewall.enabled = Services.prefs.getBoolPref(
    
    665
    +      TorSettingsPrefs.firewall.enabled,
    
    666
    +      false
    
    667
    +    );
    
    668
    +    if (this.firewall.enabled) {
    
    669
    +      this.firewall.allowed_ports = Services.prefs.getStringPref(
    
    670
    +        TorSettingsPrefs.firewall.allowed_ports,
    
    671
    +        ""
    
    672
    +      );
    
    673
    +    }
    
    674
    +  },
    
    427 675
     
    
    428
    -      const settings = this._settings;
    
    676
    +  // save our settings to prefs
    
    677
    +  saveToPrefs() {
    
    678
    +    lazy.logger.debug("saveToPrefs()");
    
    429 679
     
    
    430
    -      /* Quickstart */
    
    431
    -      Services.prefs.setBoolPref(
    
    432
    -        TorSettingsPrefs.quickstart.enabled,
    
    433
    -        settings.quickstart.enabled
    
    434
    -      );
    
    435
    -      /* Bridges */
    
    436
    -      Services.prefs.setBoolPref(
    
    437
    -        TorSettingsPrefs.bridges.enabled,
    
    438
    -        settings.bridges.enabled
    
    680
    +    /* Quickstart */
    
    681
    +    Services.prefs.setBoolPref(
    
    682
    +      TorSettingsPrefs.quickstart.enabled,
    
    683
    +      this.quickstart.enabled
    
    684
    +    );
    
    685
    +    /* Bridges */
    
    686
    +    Services.prefs.setBoolPref(
    
    687
    +      TorSettingsPrefs.bridges.enabled,
    
    688
    +      this.bridges.enabled
    
    689
    +    );
    
    690
    +    Services.prefs.setIntPref(
    
    691
    +      TorSettingsPrefs.bridges.source,
    
    692
    +      this.bridges.source
    
    693
    +    );
    
    694
    +    Services.prefs.setStringPref(
    
    695
    +      TorSettingsPrefs.bridges.builtin_type,
    
    696
    +      this.bridges.builtin_type
    
    697
    +    );
    
    698
    +    // erase existing bridge strings
    
    699
    +    const bridgeBranchPrefs = Services.prefs
    
    700
    +      .getBranch(TorSettingsPrefs.bridges.bridge_strings)
    
    701
    +      .getChildList("");
    
    702
    +    bridgeBranchPrefs.forEach(pref => {
    
    703
    +      Services.prefs.clearUserPref(
    
    704
    +        `${TorSettingsPrefs.bridges.bridge_strings}${pref}`
    
    439 705
           );
    
    440
    -      Services.prefs.setIntPref(
    
    441
    -        TorSettingsPrefs.bridges.source,
    
    442
    -        settings.bridges.source
    
    706
    +    });
    
    707
    +    // write new ones
    
    708
    +    if (this.bridges.source !== TorBridgeSource.BuiltIn) {
    
    709
    +      this.bridges.bridge_strings.forEach((string, index) => {
    
    710
    +        Services.prefs.setStringPref(
    
    711
    +          `${TorSettingsPrefs.bridges.bridge_strings}.${index}`,
    
    712
    +          string
    
    713
    +        );
    
    714
    +      });
    
    715
    +    }
    
    716
    +    /* Proxy */
    
    717
    +    Services.prefs.setBoolPref(
    
    718
    +      TorSettingsPrefs.proxy.enabled,
    
    719
    +      this.proxy.enabled
    
    720
    +    );
    
    721
    +    if (this.proxy.enabled) {
    
    722
    +      Services.prefs.setIntPref(TorSettingsPrefs.proxy.type, this.proxy.type);
    
    723
    +      Services.prefs.setStringPref(
    
    724
    +        TorSettingsPrefs.proxy.address,
    
    725
    +        this.proxy.address
    
    443 726
           );
    
    727
    +      Services.prefs.setIntPref(TorSettingsPrefs.proxy.port, this.proxy.port);
    
    444 728
           Services.prefs.setStringPref(
    
    445
    -        TorSettingsPrefs.bridges.builtin_type,
    
    446
    -        settings.bridges.builtin_type
    
    729
    +        TorSettingsPrefs.proxy.username,
    
    730
    +        this.proxy.username
    
    447 731
           );
    
    448
    -      // erase existing bridge strings
    
    449
    -      const bridgeBranchPrefs = Services.prefs
    
    450
    -        .getBranch(TorSettingsPrefs.bridges.bridge_strings)
    
    451
    -        .getChildList("");
    
    452
    -      bridgeBranchPrefs.forEach(pref => {
    
    453
    -        Services.prefs.clearUserPref(
    
    454
    -          `${TorSettingsPrefs.bridges.bridge_strings}${pref}`
    
    455
    -        );
    
    456
    -      });
    
    457
    -      // write new ones
    
    458
    -      if (settings.bridges.source !== TorBridgeSource.BuiltIn) {
    
    459
    -        settings.bridges.bridge_strings.forEach((string, index) => {
    
    460
    -          Services.prefs.setStringPref(
    
    461
    -            `${TorSettingsPrefs.bridges.bridge_strings}.${index}`,
    
    462
    -            string
    
    463
    -          );
    
    464
    -        });
    
    465
    -      }
    
    466
    -      /* Proxy */
    
    467
    -      Services.prefs.setBoolPref(
    
    468
    -        TorSettingsPrefs.proxy.enabled,
    
    469
    -        settings.proxy.enabled
    
    732
    +      Services.prefs.setStringPref(
    
    733
    +        TorSettingsPrefs.proxy.password,
    
    734
    +        this.proxy.password
    
    470 735
           );
    
    471
    -      if (settings.proxy.enabled) {
    
    472
    -        Services.prefs.setIntPref(
    
    473
    -          TorSettingsPrefs.proxy.type,
    
    474
    -          settings.proxy.type
    
    475
    -        );
    
    476
    -        Services.prefs.setStringPref(
    
    477
    -          TorSettingsPrefs.proxy.address,
    
    478
    -          settings.proxy.address
    
    479
    -        );
    
    480
    -        Services.prefs.setIntPref(
    
    481
    -          TorSettingsPrefs.proxy.port,
    
    482
    -          settings.proxy.port
    
    483
    -        );
    
    484
    -        Services.prefs.setStringPref(
    
    485
    -          TorSettingsPrefs.proxy.username,
    
    486
    -          settings.proxy.username
    
    487
    -        );
    
    488
    -        Services.prefs.setStringPref(
    
    489
    -          TorSettingsPrefs.proxy.password,
    
    490
    -          settings.proxy.password
    
    491
    -        );
    
    492
    -      } else {
    
    493
    -        Services.prefs.clearUserPref(TorSettingsPrefs.proxy.type);
    
    494
    -        Services.prefs.clearUserPref(TorSettingsPrefs.proxy.address);
    
    495
    -        Services.prefs.clearUserPref(TorSettingsPrefs.proxy.port);
    
    496
    -        Services.prefs.clearUserPref(TorSettingsPrefs.proxy.username);
    
    497
    -        Services.prefs.clearUserPref(TorSettingsPrefs.proxy.password);
    
    498
    -      }
    
    499
    -      /* Firewall */
    
    500
    -      Services.prefs.setBoolPref(
    
    501
    -        TorSettingsPrefs.firewall.enabled,
    
    502
    -        settings.firewall.enabled
    
    736
    +    } else {
    
    737
    +      Services.prefs.clearUserPref(TorSettingsPrefs.proxy.type);
    
    738
    +      Services.prefs.clearUserPref(TorSettingsPrefs.proxy.address);
    
    739
    +      Services.prefs.clearUserPref(TorSettingsPrefs.proxy.port);
    
    740
    +      Services.prefs.clearUserPref(TorSettingsPrefs.proxy.username);
    
    741
    +      Services.prefs.clearUserPref(TorSettingsPrefs.proxy.password);
    
    742
    +    }
    
    743
    +    /* Firewall */
    
    744
    +    Services.prefs.setBoolPref(
    
    745
    +      TorSettingsPrefs.firewall.enabled,
    
    746
    +      this.firewall.enabled
    
    747
    +    );
    
    748
    +    if (this.firewall.enabled) {
    
    749
    +      Services.prefs.setStringPref(
    
    750
    +        TorSettingsPrefs.firewall.allowed_ports,
    
    751
    +        this.firewall.allowed_ports.join(",")
    
    503 752
           );
    
    504
    -      if (settings.firewall.enabled) {
    
    505
    -        Services.prefs.setStringPref(
    
    506
    -          TorSettingsPrefs.firewall.allowed_ports,
    
    507
    -          settings.firewall.allowed_ports.join(",")
    
    508
    -        );
    
    509
    -      } else {
    
    510
    -        Services.prefs.clearUserPref(TorSettingsPrefs.firewall.allowed_ports);
    
    511
    -      }
    
    753
    +    } else {
    
    754
    +      Services.prefs.clearUserPref(TorSettingsPrefs.firewall.allowed_ports);
    
    755
    +    }
    
    512 756
     
    
    513
    -      // all tor settings now stored in prefs :)
    
    514
    -      Services.prefs.setBoolPref(TorSettingsPrefs.enabled, true);
    
    757
    +    // all tor settings now stored in prefs :)
    
    758
    +    Services.prefs.setBoolPref(TorSettingsPrefs.enabled, true);
    
    515 759
     
    
    516
    -      return this;
    
    517
    -    },
    
    518
    -
    
    519
    -    // push our settings down to the tor daemon
    
    520
    -    async applySettings() {
    
    521
    -      console.log("TorSettings: applySettings()");
    
    522
    -      const settings = this._settings;
    
    523
    -      const settingsMap = new Map();
    
    524
    -
    
    525
    -      /* Bridges */
    
    526
    -      const haveBridges =
    
    527
    -        settings.bridges.enabled && !!settings.bridges.bridge_strings.length;
    
    528
    -      settingsMap.set(TorConfigKeys.useBridges, haveBridges);
    
    529
    -      if (haveBridges) {
    
    530
    -        settingsMap.set(
    
    531
    -          TorConfigKeys.bridgeList,
    
    532
    -          settings.bridges.bridge_strings
    
    533
    -        );
    
    534
    -      } else {
    
    535
    -        settingsMap.set(TorConfigKeys.bridgeList, null);
    
    536
    -      }
    
    760
    +    return this;
    
    761
    +  },
    
    537 762
     
    
    538
    -      /* Proxy */
    
    539
    -      settingsMap.set(TorConfigKeys.socks4Proxy, null);
    
    540
    -      settingsMap.set(TorConfigKeys.socks5Proxy, null);
    
    541
    -      settingsMap.set(TorConfigKeys.socks5ProxyUsername, null);
    
    542
    -      settingsMap.set(TorConfigKeys.socks5ProxyPassword, null);
    
    543
    -      settingsMap.set(TorConfigKeys.httpsProxy, null);
    
    544
    -      settingsMap.set(TorConfigKeys.httpsProxyAuthenticator, null);
    
    545
    -      if (settings.proxy.enabled) {
    
    546
    -        const address = settings.proxy.address;
    
    547
    -        const port = settings.proxy.port;
    
    548
    -        const username = settings.proxy.username;
    
    549
    -        const password = settings.proxy.password;
    
    550
    -
    
    551
    -        switch (settings.proxy.type) {
    
    552
    -          case TorProxyType.Socks4:
    
    553
    -            settingsMap.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
    
    554
    -            break;
    
    555
    -          case TorProxyType.Socks5:
    
    556
    -            settingsMap.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
    
    557
    -            settingsMap.set(TorConfigKeys.socks5ProxyUsername, username);
    
    558
    -            settingsMap.set(TorConfigKeys.socks5ProxyPassword, password);
    
    559
    -            break;
    
    560
    -          case TorProxyType.HTTPS:
    
    561
    -            settingsMap.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
    
    562
    -            settingsMap.set(
    
    563
    -              TorConfigKeys.httpsProxyAuthenticator,
    
    564
    -              `${username}:${password}`
    
    565
    -            );
    
    566
    -            break;
    
    567
    -        }
    
    568
    -      }
    
    763
    +  // push our settings down to the tor daemon
    
    764
    +  async applySettings() {
    
    765
    +    lazy.logger.debug("applySettings()");
    
    766
    +    const settingsMap = new Map();
    
    767
    +
    
    768
    +    /* Bridges */
    
    769
    +    const haveBridges =
    
    770
    +      this.bridges.enabled && !!this.bridges.bridge_strings.length;
    
    771
    +    settingsMap.set(TorConfigKeys.useBridges, haveBridges);
    
    772
    +    if (haveBridges) {
    
    773
    +      settingsMap.set(TorConfigKeys.bridgeList, this.bridges.bridge_strings);
    
    774
    +    } else {
    
    775
    +      settingsMap.set(TorConfigKeys.bridgeList, null);
    
    776
    +    }
    
    569 777
     
    
    570
    -      /* Firewall */
    
    571
    -      if (settings.firewall.enabled) {
    
    572
    -        const reachableAddresses = settings.firewall.allowed_ports
    
    573
    -          .map(port => `*:${port}`)
    
    574
    -          .join(",");
    
    575
    -        settingsMap.set(TorConfigKeys.reachableAddresses, reachableAddresses);
    
    576
    -      } else {
    
    577
    -        settingsMap.set(TorConfigKeys.reachableAddresses, null);
    
    778
    +    /* Proxy */
    
    779
    +    settingsMap.set(TorConfigKeys.socks4Proxy, null);
    
    780
    +    settingsMap.set(TorConfigKeys.socks5Proxy, null);
    
    781
    +    settingsMap.set(TorConfigKeys.socks5ProxyUsername, null);
    
    782
    +    settingsMap.set(TorConfigKeys.socks5ProxyPassword, null);
    
    783
    +    settingsMap.set(TorConfigKeys.httpsProxy, null);
    
    784
    +    settingsMap.set(TorConfigKeys.httpsProxyAuthenticator, null);
    
    785
    +    if (this.proxy.enabled) {
    
    786
    +      const address = this.proxy.address;
    
    787
    +      const port = this.proxy.port;
    
    788
    +      const username = this.proxy.username;
    
    789
    +      const password = this.proxy.password;
    
    790
    +
    
    791
    +      switch (this.proxy.type) {
    
    792
    +        case TorProxyType.Socks4:
    
    793
    +          settingsMap.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
    
    794
    +          break;
    
    795
    +        case TorProxyType.Socks5:
    
    796
    +          settingsMap.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
    
    797
    +          settingsMap.set(TorConfigKeys.socks5ProxyUsername, username);
    
    798
    +          settingsMap.set(TorConfigKeys.socks5ProxyPassword, password);
    
    799
    +          break;
    
    800
    +        case TorProxyType.HTTPS:
    
    801
    +          settingsMap.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
    
    802
    +          settingsMap.set(
    
    803
    +            TorConfigKeys.httpsProxyAuthenticator,
    
    804
    +            `${username}:${password}`
    
    805
    +          );
    
    806
    +          break;
    
    578 807
           }
    
    808
    +    }
    
    579 809
     
    
    580
    -      /* Push to Tor */
    
    581
    -      const provider = await lazy.TorProviderBuilder.build();
    
    582
    -      await provider.writeSettings(settingsMap);
    
    810
    +    /* Firewall */
    
    811
    +    if (this.firewall.enabled) {
    
    812
    +      const reachableAddresses = this.firewall.allowed_ports
    
    813
    +        .map(port => `*:${port}`)
    
    814
    +        .join(",");
    
    815
    +      settingsMap.set(TorConfigKeys.reachableAddresses, reachableAddresses);
    
    816
    +    } else {
    
    817
    +      settingsMap.set(TorConfigKeys.reachableAddresses, null);
    
    818
    +    }
    
    583 819
     
    
    584
    -      return this;
    
    585
    -    },
    
    820
    +    /* Push to Tor */
    
    821
    +    const provider = await lazy.TorProviderBuilder.build();
    
    822
    +    await provider.writeSettings(settingsMap);
    
    586 823
     
    
    587
    -    // set all of our settings at once from a settings object
    
    588
    -    setSettings(settings) {
    
    589
    -      console.log("TorSettings: setSettings()");
    
    590
    -      const backup = this.getSettings();
    
    824
    +    return this;
    
    825
    +  },
    
    591 826
     
    
    592
    -      try {
    
    593
    -        this._settings.bridges.enabled = !!settings.bridges.enabled;
    
    594
    -        this._settings.bridges.source = settings.bridges.source;
    
    595
    -        switch (settings.bridges.source) {
    
    596
    -          case TorBridgeSource.BridgeDB:
    
    597
    -          case TorBridgeSource.UserProvided:
    
    598
    -            this._settings.bridges.bridge_strings =
    
    599
    -              settings.bridges.bridge_strings;
    
    600
    -            break;
    
    601
    -          case TorBridgeSource.BuiltIn: {
    
    602
    -            this._settings.bridges.builtin_type = settings.bridges.builtin_type;
    
    603
    -            settings.bridges.bridge_strings = getBuiltinBridgeStrings(
    
    604
    -              settings.bridges.builtin_type
    
    827
    +  // set all of our settings at once from a settings object
    
    828
    +  setSettings(settings) {
    
    829
    +    lazy.logger.debug("setSettings()");
    
    830
    +    const backup = this.getSettings();
    
    831
    +    const backup_notifications = [...this._notificationQueue];
    
    832
    +
    
    833
    +    // Hold off on lots of notifications until all settings are changed.
    
    834
    +    this.freezeNotifications();
    
    835
    +    try {
    
    836
    +      this.bridges.enabled = !!settings.bridges.enabled;
    
    837
    +      this.bridges.source = settings.bridges.source;
    
    838
    +      switch (settings.bridges.source) {
    
    839
    +        case TorBridgeSource.BridgeDB:
    
    840
    +        case TorBridgeSource.UserProvided:
    
    841
    +          this.bridges.bridge_strings = settings.bridges.bridge_strings;
    
    842
    +          break;
    
    843
    +        case TorBridgeSource.BuiltIn: {
    
    844
    +          this.bridges.builtin_type = settings.bridges.builtin_type;
    
    845
    +          if (!this.bridges.bridge_strings.length) {
    
    846
    +            // No bridges were found when setting the builtin_type.
    
    847
    +            throw new Error(
    
    848
    +              `No available builtin bridges of type ${settings.bridges.builtin_type}`
    
    605 849
                 );
    
    606
    -            if (
    
    607
    -              !settings.bridges.bridge_strings.length &&
    
    608
    -              settings.bridges.enabled
    
    609
    -            ) {
    
    610
    -              throw new Error(
    
    611
    -                `No available builtin bridges of type ${settings.bridges.builtin_type}`
    
    612
    -              );
    
    613
    -            }
    
    614
    -            this._settings.bridges.bridge_strings =
    
    615
    -              settings.bridges.bridge_strings;
    
    616
    -            break;
    
    617 850
               }
    
    618
    -          case TorBridgeSource.Invalid:
    
    619
    -            break;
    
    620
    -          default:
    
    621
    -            if (settings.bridges.enabled) {
    
    622
    -              throw new Error(
    
    623
    -                `Bridge source '${settings.source}' is not a valid source`
    
    624
    -              );
    
    625
    -            }
    
    626
    -            break;
    
    851
    +          break;
    
    627 852
             }
    
    628
    -
    
    629
    -        // TODO: proxy and firewall
    
    630
    -      } catch (ex) {
    
    631
    -        this._settings = backup;
    
    632
    -        console.log(`TorSettings: setSettings failed => ${ex.message}`);
    
    633
    -      }
    
    634
    -
    
    635
    -      console.log("TorSettings: setSettings result");
    
    636
    -      console.log(this._settings);
    
    637
    -    },
    
    638
    -
    
    639
    -    // get a copy of all our settings
    
    640
    -    getSettings() {
    
    641
    -      console.log("TorSettings: getSettings()");
    
    642
    -      // TODO: replace with structuredClone someday (post esr94): https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
    
    643
    -      return JSON.parse(JSON.stringify(this._settings));
    
    644
    -    },
    
    645
    -
    
    646
    -    /* Getters and Setters */
    
    647
    -
    
    648
    -    // Quickstart
    
    649
    -    get quickstart() {
    
    650
    -      return {
    
    651
    -        get enabled() {
    
    652
    -          return self._settings.quickstart.enabled;
    
    653
    -        },
    
    654
    -        set enabled(val) {
    
    655
    -          if (val != self._settings.quickstart.enabled) {
    
    656
    -            self._settings.quickstart.enabled = val;
    
    657
    -            Services.obs.notifyObservers(
    
    658
    -              { value: val },
    
    659
    -              TorSettingsTopics.SettingChanged,
    
    660
    -              TorSettingsData.QuickStartEnabled
    
    853
    +        case TorBridgeSource.Invalid:
    
    854
    +          break;
    
    855
    +        default:
    
    856
    +          if (settings.bridges.enabled) {
    
    857
    +            throw new Error(
    
    858
    +              `Bridge source '${settings.source}' is not a valid source`
    
    661 859
                 );
    
    662 860
               }
    
    663
    -        },
    
    664
    -      };
    
    665
    -    },
    
    861
    +          break;
    
    862
    +      }
    
    666 863
     
    
    667
    -    // Bridges
    
    668
    -    get bridges() {
    
    669
    -      return {
    
    670
    -        get enabled() {
    
    671
    -          return self._settings.bridges.enabled;
    
    672
    -        },
    
    673
    -        set enabled(val) {
    
    674
    -          self._settings.bridges.enabled = val;
    
    675
    -        },
    
    676
    -        get source() {
    
    677
    -          return self._settings.bridges.source;
    
    678
    -        },
    
    679
    -        set source(val) {
    
    680
    -          self._settings.bridges.source = val;
    
    681
    -        },
    
    682
    -        get builtin_type() {
    
    683
    -          return self._settings.bridges.builtin_type;
    
    684
    -        },
    
    685
    -        set builtin_type(val) {
    
    686
    -          const bridgeStrings = getBuiltinBridgeStrings(val);
    
    687
    -          if (bridgeStrings.length) {
    
    688
    -            self._settings.bridges.builtin_type = val;
    
    689
    -            self._settings.bridges.bridge_strings = bridgeStrings;
    
    690
    -          } else {
    
    691
    -            self._settings.bridges.builtin_type = "";
    
    692
    -            if (self._settings.bridges.source === TorBridgeSource.BuiltIn) {
    
    693
    -              self._settings.bridges.source = TorBridgeSource.Invalid;
    
    694
    -            }
    
    695
    -          }
    
    696
    -        },
    
    697
    -        get bridge_strings() {
    
    698
    -          return arrayCopy(self._settings.bridges.bridge_strings);
    
    699
    -        },
    
    700
    -        set bridge_strings(val) {
    
    701
    -          self._settings.bridges.bridge_strings = parseBridgeStrings(val);
    
    702
    -        },
    
    703
    -      };
    
    704
    -    },
    
    864
    +      // TODO: proxy and firewall
    
    865
    +    } catch (ex) {
    
    866
    +      // Restore the old settings without any new notifications generated from
    
    867
    +      // the above code.
    
    868
    +      // NOTE: Since this code is not async, it should not be possible for
    
    869
    +      // some other call to TorSettings to change anything whilst we are
    
    870
    +      // in this context (other than lower down in this call stack), so it is
    
    871
    +      // safe to discard all changes to settings and notifications.
    
    872
    +      this._settings = backup;
    
    873
    +      this._notificationQueue.clear();
    
    874
    +      for (const notification of backup_notifications) {
    
    875
    +        this._notificationQueue.add(notification);
    
    876
    +      }
    
    705 877
     
    
    706
    -    // Proxy
    
    707
    -    get proxy() {
    
    708
    -      return {
    
    709
    -        get enabled() {
    
    710
    -          return self._settings.proxy.enabled;
    
    711
    -        },
    
    712
    -        set enabled(val) {
    
    713
    -          self._settings.proxy.enabled = val;
    
    714
    -          // reset proxy settings
    
    715
    -          self._settings.proxy.type = TorProxyType.Invalid;
    
    716
    -          self._settings.proxy.address = null;
    
    717
    -          self._settings.proxy.port = 0;
    
    718
    -          self._settings.proxy.username = null;
    
    719
    -          self._settings.proxy.password = null;
    
    720
    -        },
    
    721
    -        get type() {
    
    722
    -          return self._settings.proxy.type;
    
    723
    -        },
    
    724
    -        set type(val) {
    
    725
    -          self._settings.proxy.type = val;
    
    726
    -        },
    
    727
    -        get address() {
    
    728
    -          return self._settings.proxy.address;
    
    729
    -        },
    
    730
    -        set address(val) {
    
    731
    -          self._settings.proxy.address = val;
    
    732
    -        },
    
    733
    -        get port() {
    
    734
    -          return arrayCopy(self._settings.proxy.port);
    
    735
    -        },
    
    736
    -        set port(val) {
    
    737
    -          self._settings.proxy.port = parsePort(val);
    
    738
    -        },
    
    739
    -        get username() {
    
    740
    -          return self._settings.proxy.username;
    
    741
    -        },
    
    742
    -        set username(val) {
    
    743
    -          self._settings.proxy.username = val;
    
    744
    -        },
    
    745
    -        get password() {
    
    746
    -          return self._settings.proxy.password;
    
    747
    -        },
    
    748
    -        set password(val) {
    
    749
    -          self._settings.proxy.password = val;
    
    750
    -        },
    
    751
    -        get uri() {
    
    752
    -          switch (this.type) {
    
    753
    -            case TorProxyType.Socks4:
    
    754
    -              return `socks4a://${this.address}:${this.port}`;
    
    755
    -            case TorProxyType.Socks5:
    
    756
    -              if (this.username) {
    
    757
    -                return `socks5://${this.username}:${this.password}@${this.address}:${this.port}`;
    
    758
    -              }
    
    759
    -              return `socks5://${this.address}:${this.port}`;
    
    760
    -            case TorProxyType.HTTPS:
    
    761
    -              if (this._proxyUsername) {
    
    762
    -                return `http://${this.username}:${this.password}@${this.address}:${this.port}`;
    
    763
    -              }
    
    764
    -              return `http://${this.address}:${this.port}`;
    
    765
    -          }
    
    766
    -          return null;
    
    767
    -        },
    
    768
    -      };
    
    769
    -    },
    
    878
    +      lazy.logger.error("setSettings failed", ex);
    
    879
    +    } finally {
    
    880
    +      this.thawNotifications();
    
    881
    +    }
    
    770 882
     
    
    771
    -    // Firewall
    
    772
    -    get firewall() {
    
    773
    -      return {
    
    774
    -        get enabled() {
    
    775
    -          return self._settings.firewall.enabled;
    
    776
    -        },
    
    777
    -        set enabled(val) {
    
    778
    -          self._settings.firewall.enabled = val;
    
    779
    -          // reset firewall settings
    
    780
    -          self._settings.firewall.allowed_ports = [];
    
    781
    -        },
    
    782
    -        get allowed_ports() {
    
    783
    -          return self._settings.firewall.allowed_ports;
    
    784
    -        },
    
    785
    -        set allowed_ports(val) {
    
    786
    -          self._settings.firewall.allowed_ports = parsePortList(val);
    
    787
    -        },
    
    788
    -      };
    
    789
    -    },
    
    790
    -  };
    
    791
    -  return self;
    
    792
    -})();
    883
    +    lazy.logger.debug("setSettings result", this._settings);
    
    884
    +  },
    
    885
    +
    
    886
    +  // get a copy of all our settings
    
    887
    +  getSettings() {
    
    888
    +    lazy.logger.debug("getSettings()");
    
    889
    +    return structuredClone(this._settings);
    
    890
    +  },
    
    891
    +};

  • _______________________________________________
    tor-commits mailing list
    tor-commits@xxxxxxxxxxxxxxxxxxxx
    https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits