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

Commits:

9 changed files:

Changes:

  • browser/app/profile/000-tor-browser.js
    ... ... @@ -69,7 +69,6 @@ pref("extensions.torbutton.pref_fixup_version", 0);
    69 69
     
    
    70 70
     pref("extensions.torlauncher.start_tor", true);
    
    71 71
     pref("extensions.torlauncher.prompt_at_startup", true);
    
    72
    -pref("extensions.torlauncher.quickstart", false);
    
    73 72
     
    
    74 73
     pref("extensions.torlauncher.max_tor_log_entries", 1000);
    
    75 74
     
    
    ... ... @@ -113,11 +112,3 @@ pref("extensions.torlauncher.tordatadir_path", "");
    113 112
     pref("extensions.torlauncher.bridgedb_front", "foursquare.com");
    
    114 113
     pref("extensions.torlauncher.bridgedb_reflector", "https://moat.torproject.org.global.prod.fastly.net/");
    
    115 114
     pref("extensions.torlauncher.moat_service", "https://bridges.torproject.org/moat");
    116
    -pref("extensions.torlauncher.bridgedb_bridge_type", "obfs4");
    
    117
    -
    
    118
    -// Recommended default bridge type.
    
    119
    -// pref("extensions.torlauncher.default_bridge_recommended_type", "obfs3");
    
    120
    -
    
    121
    -// Default bridges.
    
    122
    -// pref("extensions.torlauncher.default_bridge.TYPE.1", "TYPE x.x.x.x:yy");
    
    123
    -// pref("extensions.torlauncher.default_bridge.TYPE.2", "TYPE x.x.x.x:yy");

  • browser/components/torpreferences/content/builtinBridgeDialog.mjs
    ... ... @@ -3,7 +3,6 @@ import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs";
    3 3
     import {
    
    4 4
       TorSettings,
    
    5 5
       TorBridgeSource,
    
    6
    -  TorBuiltinBridgeTypes,
    
    7 6
     } from "resource://gre/modules/TorSettings.sys.mjs";
    
    8 7
     
    
    9 8
     import {
    
    ... ... @@ -62,7 +61,7 @@ export class BuiltinBridgeDialog {
    62 61
         )) {
    
    63 62
           const radio = optionEl.querySelector("radio");
    
    64 63
           const type = radio.value;
    
    65
    -      optionEl.hidden = !TorBuiltinBridgeTypes.includes(type);
    
    64
    +      optionEl.hidden = !TorSettings.builtinBridgeTypes.includes(type);
    
    66 65
           radio.label = typeStrings[type].label;
    
    67 66
           optionEl.querySelector(
    
    68 67
             ".builtin-bridges-option-description"
    

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -867,7 +867,7 @@ const gConnectionPane = (function () {
    867 867
         },
    
    868 868
     
    
    869 869
         init() {
    
    870
    -      this._populateXUL();
    
    870
    +      TorSettings.initializedPromise.then(() => this._populateXUL());
    
    871 871
     
    
    872 872
           const onUnload = () => {
    
    873 873
             window.removeEventListener("unload", onUnload);
    

  • toolkit/content/jar.mn
    ... ... @@ -137,3 +137,5 @@ toolkit.jar:
    137 137
     # Third party files
    
    138 138
        content/global/third_party/d3/d3.js                (/third_party/js/d3/d3.js)
    
    139 139
        content/global/third_party/cfworker/json-schema.js (/third_party/js/cfworker/json-schema.js)
    
    140
    +
    
    141
    +   content/global/pt_config.json               (pt_config.json)

  • toolkit/content/pt_config.json
    1
    +{
    
    2
    +  "_comment": "Used for dev build, replaced for release builds in tor-browser-build. This file is copied from tor-browser-build cb513eec:tor-expert-bundle/pt_config.json",
    
    3
    +  "recommendedDefault" : "obfs4",
    
    4
    +  "pluggableTransports" : {
    
    5
    +    "lyrebird" : "ClientTransportPlugin meek_lite,obfs2,obfs3,obfs4,scramblesuit exec ${pt_path}lyrebird${pt_extension}",
    
    6
    +    "snowflake" : "ClientTransportPlugin snowflake exec ${pt_path}snowflake-client${pt_extension}",
    
    7
    +    "webtunnel" : "ClientTransportPlugin webtunnel exec ${pt_path}webtunnel-client${pt_extension}",
    
    8
    +    "conjure" : "ClientTransportPlugin conjure exec ${pt_path}conjure-client${pt_extension} -registerURL https://registration.refraction.network/api"
    
    9
    +  },
    
    10
    +  "bridges" : {
    
    11
    +    "meek-azure" : [
    
    12
    +      "meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com"
    
    13
    +    ],
    
    14
    +    "obfs4" : [
    
    15
    +      "obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1",
    
    16
    +      "obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0",
    
    17
    +      "obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0",
    
    18
    +      "obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0",
    
    19
    +      "obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0",
    
    20
    +      "obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0",
    
    21
    +      "obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0",
    
    22
    +      "obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0",
    
    23
    +      "obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0",
    
    24
    +      "obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0",
    
    25
    +      "obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0"
    
    26
    +    ],
    
    27
    +    "snowflake" : [
    
    28
    +      "snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=foursquare.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn",
    
    29
    +      "snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=foursquare.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"
    
    30
    +    ]
    
    31
    +  }
    
    32
    +}

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -23,7 +23,6 @@ import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs"
    23 23
     import {
    
    24 24
       TorSettings,
    
    25 25
       TorSettingsTopics,
    
    26
    -  TorBuiltinBridgeTypes,
    
    27 26
     } from "resource://gre/modules/TorSettings.sys.mjs";
    
    28 27
     
    
    29 28
     import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs";
    
    ... ... @@ -609,7 +608,7 @@ export const TorConnect = (() => {
    609 608
                     }
    
    610 609
     
    
    611 610
                     const settings = await this.mrpc.circumvention_settings(
    
    612
    -                  [...TorBuiltinBridgeTypes, "vanilla"],
    
    611
    +                  [...TorSettings.builtinBridgeTypes, "vanilla"],
    
    613 612
                       countryCode
    
    614 613
                     );
    
    615 614
     
    
    ... ... @@ -625,7 +624,7 @@ export const TorConnect = (() => {
    625 624
                     } else {
    
    626 625
                       try {
    
    627 626
                         this.settings = await this.mrpc.circumvention_defaults([
    
    628
    -                      ...TorBuiltinBridgeTypes,
    
    627
    +                      ...TorSettings.builtinBridgeTypes,
    
    629 628
                           "vanilla",
    
    630 629
                         ]);
    
    631 630
                       } catch (err) {
    

  • toolkit/modules/TorSettings.sys.mjs
    ... ... @@ -67,16 +67,6 @@ const TorSettingsPrefs = Object.freeze({
    67 67
       },
    
    68 68
     });
    
    69 69
     
    
    70
    -/* Legacy tor-launcher prefs and pref branches*/
    
    71
    -const TorLauncherPrefs = Object.freeze({
    
    72
    -  quickstart: "extensions.torlauncher.quickstart",
    
    73
    -  default_bridge_type: "extensions.torlauncher.default_bridge_type",
    
    74
    -  default_bridge: "extensions.torlauncher.default_bridge.",
    
    75
    -  default_bridge_recommended_type:
    
    76
    -    "extensions.torlauncher.default_bridge_recommended_type",
    
    77
    -  bridgedb_bridge: "extensions.torlauncher.bridgedb_bridge.",
    
    78
    -});
    
    79
    -
    
    80 70
     /* Config Keys used to configure tor daemon */
    
    81 71
     const TorConfigKeys = Object.freeze({
    
    82 72
       useBridges: "UseBridges",
    
    ... ... @@ -105,101 +95,41 @@ export const TorProxyType = Object.freeze({
    105 95
       HTTPS: 2,
    
    106 96
     });
    
    107 97
     
    
    108
    -export const TorBuiltinBridgeTypes = Object.freeze(
    
    109
    -  (() => {
    
    110
    -    const bridgeListBranch = Services.prefs.getBranch(
    
    111
    -      TorLauncherPrefs.default_bridge
    
    112
    -    );
    
    113
    -    const bridgePrefs = bridgeListBranch.getChildList("");
    
    114
    -
    
    115
    -    // an unordered set for shoving bridge types into
    
    116
    -    const bridgeTypes = new Set();
    
    117
    -    // look for keys ending in ".N" and treat string before that as the bridge type
    
    118
    -    const pattern = /\.[0-9]+$/;
    
    119
    -    for (const key of bridgePrefs) {
    
    120
    -      const offset = key.search(pattern);
    
    121
    -      if (offset != -1) {
    
    122
    -        const bt = key.substring(0, offset);
    
    123
    -        bridgeTypes.add(bt);
    
    124
    -      }
    
    125
    -    }
    
    126
    -
    
    127
    -    // recommended bridge type goes first in the list
    
    128
    -    const recommendedBridgeType = Services.prefs.getCharPref(
    
    129
    -      TorLauncherPrefs.default_bridge_recommended_type,
    
    130
    -      null
    
    131
    -    );
    
    132
    -
    
    133
    -    const retval = [];
    
    134
    -    if (recommendedBridgeType && bridgeTypes.has(recommendedBridgeType)) {
    
    135
    -      retval.push(recommendedBridgeType);
    
    136
    -    }
    
    137
    -
    
    138
    -    for (const bridgeType of bridgeTypes.values()) {
    
    139
    -      if (bridgeType != recommendedBridgeType) {
    
    140
    -        retval.push(bridgeType);
    
    141
    -      }
    
    142
    -    }
    
    143
    -    return retval;
    
    144
    -  })()
    
    145
    -);
    
    146
    -
    
    147
    -/* Parsing Methods */
    
    148
    -
    
    149
    -// expects a '\n' or '\r\n' delimited bridge string, which we split and trim
    
    150
    -// each bridge string can also optionally have 'bridge' at the beginning ie:
    
    151
    -// bridge $(type) $(address):$(port) $(certificate)
    
    152
    -// we strip out the 'bridge' prefix here
    
    153
    -const parseBridgeStrings = function (aBridgeStrings) {
    
    98
    +/**
    
    99
    + * Split a blob of bridge lines into an array with single lines.
    
    100
    + * Lines are delimited by \r\n or \n and each bridge string can also optionally
    
    101
    + * have 'bridge' at the beginning.
    
    102
    + * We split the text by \r\n, we trim the lines, remove the bridge prefix and
    
    103
    + * filter out any remaiing empty item.
    
    104
    + *
    
    105
    + * @param {string} aBridgeStrings The text with the lines
    
    106
    + * @returns {string[]} An array where each bridge line is an item
    
    107
    + */
    
    108
    +function parseBridgeStrings(aBridgeStrings) {
    
    154 109
       // replace carriage returns ('\r') with new lines ('\n')
    
    155 110
       aBridgeStrings = aBridgeStrings.replace(/\r/g, "\n");
    
    156 111
       // then replace contiguous new lines ('\n') with a single one
    
    157 112
       aBridgeStrings = aBridgeStrings.replace(/[\n]+/g, "\n");
    
    158 113
     
    
    159
    -  // split on the newline and for each bridge string: trim, remove starting 'bridge' string
    
    160
    -  // finally discard entries that are empty strings; empty strings could occur if we receive
    
    161
    -  // a new line containing only whitespace
    
    114
    +  // Split on the newline and for each bridge string: trim, remove starting
    
    115
    +  // 'bridge' string.
    
    116
    +  // Finally, discard entries that are empty strings; empty strings could occur
    
    117
    +  // if we receive a new line containing only whitespace.
    
    162 118
       const splitStrings = aBridgeStrings.split("\n");
    
    163 119
       return splitStrings
    
    164 120
         .map(val => val.trim().replace(/^bridge\s+/i, ""))
    
    165
    -    .filter(bridgeString => bridgeString != "");
    
    166
    -};
    
    167
    -
    
    168
    -const getBuiltinBridgeStrings = function (builtinType) {
    
    169
    -  if (!builtinType) {
    
    170
    -    return [];
    
    171
    -  }
    
    172
    -
    
    173
    -  const bridgeBranch = Services.prefs.getBranch(
    
    174
    -    TorLauncherPrefs.default_bridge
    
    175
    -  );
    
    176
    -  const bridgeBranchPrefs = bridgeBranch.getChildList("");
    
    177
    -  const retval = [];
    
    178
    -
    
    179
    -  // regex matches against strings ending in ".N" where N is a positive integer
    
    180
    -  const pattern = /\.[0-9]+$/;
    
    181
    -  for (const key of bridgeBranchPrefs) {
    
    182
    -    // verify the location of the match is the correct offset required for aBridgeType
    
    183
    -    // to fit, and that the string begins with aBridgeType
    
    184
    -    if (
    
    185
    -      key.search(pattern) == builtinType.length &&
    
    186
    -      key.startsWith(builtinType)
    
    187
    -    ) {
    
    188
    -      const bridgeStr = bridgeBranch.getCharPref(key);
    
    189
    -      retval.push(bridgeStr);
    
    190
    -    }
    
    191
    -  }
    
    192
    -
    
    193
    -  // shuffle so that Tor Browser users don't all try the built-in bridges in the same order
    
    194
    -  arrayShuffle(retval);
    
    195
    -
    
    196
    -  return retval;
    
    197
    -};
    
    198
    -
    
    199
    -/* Helper methods */
    
    200
    -
    
    201
    -const arrayShuffle = function (array) {
    
    202
    -  // fisher-yates shuffle
    
    121
    +    .filter(bridgeString => bridgeString !== "");
    
    122
    +}
    
    123
    +
    
    124
    +/**
    
    125
    + * Return a shuffled (Fisher-Yates) copy of an array.
    
    126
    + *
    
    127
    + * @template T
    
    128
    + * @param {T[]} array
    
    129
    + * @returns {T[]}
    
    130
    + */
    
    131
    +function arrayShuffle(array) {
    
    132
    +  array = [...array];
    
    203 133
       for (let i = array.length - 1; i > 0; --i) {
    
    204 134
         // number n such that 0.0 <= n < 1.0
    
    205 135
         const n = Math.random();
    
    ... ... @@ -211,17 +141,18 @@ const arrayShuffle = function (array) {
    211 141
         array[i] = array[j];
    
    212 142
         array[j] = tmp;
    
    213 143
       }
    
    214
    -};
    
    144
    +  return array;
    
    145
    +}
    
    215 146
     
    
    216 147
     /* TorSettings module */
    
    217 148
     
    
    218
    -export const TorSettings = {
    
    149
    +class TorSettingsImpl {
    
    219 150
       /**
    
    220 151
        * The underlying settings values.
    
    221 152
        *
    
    222 153
        * @type {object}
    
    223 154
        */
    
    224
    -  _settings: {
    
    155
    +  #settings = {
    
    225 156
         quickstart: {
    
    226 157
           enabled: false,
    
    227 158
         },
    
    ... ... @@ -243,34 +174,207 @@ export const TorSettings = {
    243 174
           enabled: false,
    
    244 175
           allowed_ports: [],
    
    245 176
         },
    
    246
    -  },
    
    177
    +  };
    
    178
    +
    
    179
    +  /**
    
    180
    +   * The recommended pluggable transport.
    
    181
    +   *
    
    182
    +   * @type {string}
    
    183
    +   */
    
    184
    +  #recommendedPT = "";
    
    185
    +
    
    186
    +  /**
    
    187
    +   * The bridge lines for built-in bridges.
    
    188
    +   * Keys are pluggable transports, and values are arrays of bridge lines.
    
    189
    +   *
    
    190
    +   * @type {Object.<string, string[]>}
    
    191
    +   */
    
    192
    +  #builtinBridges = {};
    
    193
    +
    
    194
    +  /**
    
    195
    +   * Resolve callback of the initializedPromise.
    
    196
    +   */
    
    197
    +  #initComplete;
    
    198
    +  /**
    
    199
    +   * Reject callback of the initializedPromise.
    
    200
    +   */
    
    201
    +  #initFailed;
    
    202
    +  /**
    
    203
    +   * Tell whether the initializedPromise has been resolved.
    
    204
    +   * We keep this additional member to avoid making everything async.
    
    205
    +   *
    
    206
    +   * @type {boolean}
    
    207
    +   */
    
    208
    +  #initialized = false;
    
    209
    +  /**
    
    210
    +   * During some phases of the initialization, allow calling setters and
    
    211
    +   * getters without throwing errors.
    
    212
    +   *
    
    213
    +   * @type {boolean}
    
    214
    +   */
    
    215
    +  #allowUninitialized = false;
    
    216
    +
    
    217
    +  constructor() {
    
    218
    +    this.initializedPromise = new Promise((resolve, reject) => {
    
    219
    +      this.#initComplete = resolve;
    
    220
    +      this.#initFailed = reject;
    
    221
    +    });
    
    222
    +
    
    223
    +    this.#addProperties("quickstart", {
    
    224
    +      enabled: {},
    
    225
    +    });
    
    226
    +    this.#addProperties("bridges", {
    
    227
    +      enabled: {},
    
    228
    +      source: {
    
    229
    +        transform: val => {
    
    230
    +          if (Object.values(TorBridgeSource).includes(val)) {
    
    231
    +            return val;
    
    232
    +          }
    
    233
    +          lazy.logger.error(`Not a valid bridge source: "${val}"`);
    
    234
    +          return TorBridgeSource.Invalid;
    
    235
    +        },
    
    236
    +      },
    
    237
    +      bridge_strings: {
    
    238
    +        transform: val => {
    
    239
    +          if (Array.isArray(val)) {
    
    240
    +            return [...val];
    
    241
    +          }
    
    242
    +          return parseBridgeStrings(val);
    
    243
    +        },
    
    244
    +        copy: val => [...val],
    
    245
    +        equal: (val1, val2) => this.#arrayEqual(val1, val2),
    
    246
    +      },
    
    247
    +      builtin_type: {
    
    248
    +        callback: val => {
    
    249
    +          if (!val) {
    
    250
    +            // Make sure that the source is not BuiltIn
    
    251
    +            if (this.bridges.source === TorBridgeSource.BuiltIn) {
    
    252
    +              this.bridges.source = TorBridgeSource.Invalid;
    
    253
    +            }
    
    254
    +            return;
    
    255
    +          }
    
    256
    +          const bridgeStrings = this.#getBuiltinBridges(val);
    
    257
    +          if (bridgeStrings.length) {
    
    258
    +            this.bridges.bridge_strings = bridgeStrings;
    
    259
    +            return;
    
    260
    +          }
    
    261
    +          lazy.logger.error(`No built-in ${val} bridges found`);
    
    262
    +          // Change to be empty, this will trigger this callback again,
    
    263
    +          // but with val as "".
    
    264
    +          this.bridges.builtin_type == "";
    
    265
    +        },
    
    266
    +      },
    
    267
    +    });
    
    268
    +    this.#addProperties("proxy", {
    
    269
    +      enabled: {
    
    270
    +        callback: val => {
    
    271
    +          if (val) {
    
    272
    +            return;
    
    273
    +          }
    
    274
    +          // Reset proxy settings.
    
    275
    +          this.proxy.type = TorProxyType.Invalid;
    
    276
    +          this.proxy.address = "";
    
    277
    +          this.proxy.port = 0;
    
    278
    +          this.proxy.username = "";
    
    279
    +          this.proxy.password = "";
    
    280
    +        },
    
    281
    +      },
    
    282
    +      type: {
    
    283
    +        transform: val => {
    
    284
    +          if (Object.values(TorProxyType).includes(val)) {
    
    285
    +            return val;
    
    286
    +          }
    
    287
    +          lazy.logger.error(`Not a valid proxy type: "${val}"`);
    
    288
    +          return TorProxyType.Invalid;
    
    289
    +        },
    
    290
    +      },
    
    291
    +      address: {},
    
    292
    +      port: {
    
    293
    +        transform: val => {
    
    294
    +          if (val === 0) {
    
    295
    +            // This is a valid value that "unsets" the port.
    
    296
    +            // Keep this value without giving a warning.
    
    297
    +            // NOTE: In contrast, "0" is not valid.
    
    298
    +            return 0;
    
    299
    +          }
    
    300
    +          // Unset to 0 if invalid null is returned.
    
    301
    +          return this.#parsePort(val, false) ?? 0;
    
    302
    +        },
    
    303
    +      },
    
    304
    +      username: {},
    
    305
    +      password: {},
    
    306
    +      uri: {
    
    307
    +        getter: () => {
    
    308
    +          const { type, address, port, username, password } = this.proxy;
    
    309
    +          switch (type) {
    
    310
    +            case TorProxyType.Socks4:
    
    311
    +              return `socks4a://${address}:${port}`;
    
    312
    +            case TorProxyType.Socks5:
    
    313
    +              if (username) {
    
    314
    +                return `socks5://${username}:${password}@${address}:${port}`;
    
    315
    +              }
    
    316
    +              return `socks5://${address}:${port}`;
    
    317
    +            case TorProxyType.HTTPS:
    
    318
    +              if (username) {
    
    319
    +                return `http://${username}:${password}@${address}:${port}`;
    
    320
    +              }
    
    321
    +              return `http://${address}:${port}`;
    
    322
    +          }
    
    323
    +          return null;
    
    324
    +        },
    
    325
    +      },
    
    326
    +    });
    
    327
    +    this.#addProperties("firewall", {
    
    328
    +      enabled: {
    
    329
    +        callback: val => {
    
    330
    +          if (!val) {
    
    331
    +            this.firewall.allowed_ports = "";
    
    332
    +          }
    
    333
    +        },
    
    334
    +      },
    
    335
    +      allowed_ports: {
    
    336
    +        transform: val => {
    
    337
    +          if (!Array.isArray(val)) {
    
    338
    +            val = val === "" ? [] : val.split(",");
    
    339
    +          }
    
    340
    +          // parse and remove duplicates
    
    341
    +          const portSet = new Set(val.map(p => this.#parsePort(p, true)));
    
    342
    +          // parsePort returns null for failed parses, so remove it.
    
    343
    +          portSet.delete(null);
    
    344
    +          return [...portSet];
    
    345
    +        },
    
    346
    +        copy: val => [...val],
    
    347
    +        equal: (val1, val2) => this.#arrayEqual(val1, val2),
    
    348
    +      },
    
    349
    +    });
    
    350
    +  }
    
    247 351
     
    
    248 352
       /**
    
    249 353
        * The current number of freezes applied to the notifications.
    
    250 354
        *
    
    251 355
        * @type {integer}
    
    252 356
        */
    
    253
    -  _freezeNotificationsCount: 0,
    
    357
    +  #freezeNotificationsCount = 0;
    
    254 358
       /**
    
    255 359
        * The queue for settings that have changed. To be broadcast in the
    
    256 360
        * notification when not frozen.
    
    257 361
        *
    
    258 362
        * @type {Set<string>}
    
    259 363
        */
    
    260
    -  _notificationQueue: new Set(),
    
    364
    +  #notificationQueue = new Set();
    
    261 365
       /**
    
    262 366
        * Send a notification if we have any queued and we are not frozen.
    
    263 367
        */
    
    264
    -  _tryNotification() {
    
    265
    -    if (this._freezeNotificationsCount || !this._notificationQueue.size) {
    
    368
    +  #tryNotification() {
    
    369
    +    if (this.#freezeNotificationsCount || !this.#notificationQueue.size) {
    
    266 370
           return;
    
    267 371
         }
    
    268 372
         Services.obs.notifyObservers(
    
    269
    -      { changes: [...this._notificationQueue] },
    
    373
    +      { changes: [...this.#notificationQueue] },
    
    270 374
           TorSettingsTopics.SettingsChanged
    
    271 375
         );
    
    272
    -    this._notificationQueue.clear();
    
    273
    -  },
    
    376
    +    this.#notificationQueue.clear();
    
    377
    +  }
    
    274 378
       /**
    
    275 379
        * Pause notifications for changes in setting values. This is useful if you
    
    276 380
        * need to make batch changes to settings.
    
    ... ... @@ -281,8 +385,8 @@ export const TorSettings = {
    281 385
        * `finally` block.
    
    282 386
        */
    
    283 387
       freezeNotifications() {
    
    284
    -    this._freezeNotificationsCount++;
    
    285
    -  },
    
    388
    +    this.#freezeNotificationsCount++;
    
    389
    +  }
    
    286 390
       /**
    
    287 391
        * Release the hold on notifications so they may be sent out.
    
    288 392
        *
    
    ... ... @@ -290,9 +394,9 @@ export const TorSettings = {
    290 394
        * only release them once it has also called this method.
    
    291 395
        */
    
    292 396
       thawNotifications() {
    
    293
    -    this._freezeNotificationsCount--;
    
    294
    -    this._tryNotification();
    
    295
    -  },
    
    397
    +    this.#freezeNotificationsCount--;
    
    398
    +    this.#tryNotification();
    
    399
    +  }
    
    296 400
       /**
    
    297 401
        * @typedef {object} TorSettingProperty
    
    298 402
        *
    
    ... ... @@ -316,22 +420,32 @@ export const TorSettings = {
    316 420
        * @param {string} groupname - The name of the setting group. The given
    
    317 421
        *   settings will be accessible from the TorSettings property of the same
    
    318 422
        *   name.
    
    319
    -   * @param {object<string, TorSettingProperty>} propParams - An object that
    
    423
    +   * @param {object.<string, TorSettingProperty>} propParams - An object that
    
    320 424
        *   defines the settings to add to this group. The object property names
    
    321 425
        *   will be mapped to properties of TorSettings under the given groupname
    
    322 426
        *   property. Details about the setting should be described in the
    
    323 427
        *   TorSettingProperty property value.
    
    324 428
        */
    
    325
    -  _addProperties(groupname, propParams) {
    
    429
    +  #addProperties(groupname, propParams) {
    
    326 430
         // Create a new object to hold all these settings.
    
    327 431
         const group = {};
    
    328 432
         for (const name in propParams) {
    
    329 433
           const { getter, transform, callback, copy, equal } = propParams[name];
    
    330 434
           Object.defineProperty(group, name, {
    
    331 435
             get: getter
    
    332
    -          ? getter
    
    436
    +          ? () => {
    
    437
    +              // Allow getting in loadFromPrefs before we are initialized.
    
    438
    +              if (!this.#allowUninitialized) {
    
    439
    +                this.#checkIfInitialized();
    
    440
    +              }
    
    441
    +              return getter();
    
    442
    +            }
    
    333 443
               : () => {
    
    334
    -              let val = this._settings[groupname][name];
    
    444
    +              // Allow getting in loadFromPrefs before we are initialized.
    
    445
    +              if (!this.#allowUninitialized) {
    
    446
    +                this.#checkIfInitialized();
    
    447
    +              }
    
    448
    +              let val = this.#settings[groupname][name];
    
    335 449
                   if (copy) {
    
    336 450
                     val = copy(val);
    
    337 451
                   }
    
    ... ... @@ -341,7 +455,11 @@ export const TorSettings = {
    341 455
             set: getter
    
    342 456
               ? undefined
    
    343 457
               : val => {
    
    344
    -              const prevVal = this._settings[groupname][name];
    
    458
    +              // Allow setting in loadFromPrefs before we are initialized.
    
    459
    +              if (!this.#allowUninitialized) {
    
    460
    +                this.#checkIfInitialized();
    
    461
    +              }
    
    462
    +              const prevVal = this.#settings[groupname][name];
    
    345 463
                   this.freezeNotifications();
    
    346 464
                   try {
    
    347 465
                     if (transform) {
    
    ... ... @@ -352,8 +470,8 @@ export const TorSettings = {
    352 470
                       if (callback) {
    
    353 471
                         callback(val);
    
    354 472
                       }
    
    355
    -                  this._settings[groupname][name] = val;
    
    356
    -                  this._notificationQueue.add(`${groupname}.${name}`);
    
    473
    +                  this.#settings[groupname][name] = val;
    
    474
    +                  this.#notificationQueue.add(`${groupname}.${name}`);
    
    357 475
                     }
    
    358 476
                   } finally {
    
    359 477
                     this.thawNotifications();
    
    ... ... @@ -367,14 +485,14 @@ export const TorSettings = {
    367 485
           writable: false,
    
    368 486
           value: group,
    
    369 487
         });
    
    370
    -  },
    
    488
    +  }
    
    371 489
     
    
    372 490
       /**
    
    373 491
        * Regular expression for a decimal non-negative integer.
    
    374 492
        *
    
    375 493
        * @type {RegExp}
    
    376 494
        */
    
    377
    -  _portRegex: /^[0-9]+$/,
    
    495
    +  #portRegex = /^[0-9]+$/;
    
    378 496
       /**
    
    379 497
        * Parse a string as a port number.
    
    380 498
        *
    
    ... ... @@ -385,13 +503,13 @@ export const TorSettings = {
    385 503
        * @return {integer?} - The port number, or null if the given value was not
    
    386 504
        *   valid.
    
    387 505
        */
    
    388
    -  _parsePort(val, trim) {
    
    506
    +  #parsePort(val, trim) {
    
    389 507
         if (typeof val === "string") {
    
    390 508
           if (trim) {
    
    391 509
             val = val.trim();
    
    392 510
           }
    
    393 511
           // ensure port string is a valid positive integer
    
    394
    -      if (this._portRegex.test(val)) {
    
    512
    +      if (this.#portRegex.test(val)) {
    
    395 513
             val = Number.parseInt(val, 10);
    
    396 514
           } else {
    
    397 515
             lazy.logger.error(`Invalid port string "${val}"`);
    
    ... ... @@ -403,7 +521,7 @@ export const TorSettings = {
    403 521
           return null;
    
    404 522
         }
    
    405 523
         return val;
    
    406
    -  },
    
    524
    +  }
    
    407 525
       /**
    
    408 526
        * Test whether two arrays have equal members and order.
    
    409 527
        *
    
    ... ... @@ -412,142 +530,57 @@ export const TorSettings = {
    412 530
        *
    
    413 531
        * @return {boolean} - Whether the two arrays are equal.
    
    414 532
        */
    
    415
    -  _arrayEqual(val1, val2) {
    
    533
    +  #arrayEqual(val1, val2) {
    
    416 534
         if (val1.length !== val2.length) {
    
    417 535
           return false;
    
    418 536
         }
    
    419 537
         return val1.every((v, i) => v === val2[i]);
    
    420
    -  },
    
    538
    +  }
    
    539
    +
    
    540
    +  /**
    
    541
    +   * Return the bridge lines associated to a certain pluggable transport.
    
    542
    +   *
    
    543
    +   * @param {string} pt The pluggable transport to return the lines for
    
    544
    +   * @returns {string[]} The bridge lines in random order
    
    545
    +   */
    
    546
    +  #getBuiltinBridges(pt) {
    
    547
    +    // Shuffle so that Tor Browser users do not all try the built-in bridges in
    
    548
    +    // the same order.
    
    549
    +    return arrayShuffle(this.#builtinBridges[pt] ?? []);
    
    550
    +  }
    
    421 551
     
    
    422
    -  /* load or init our settings, and register observers */
    
    552
    +  /**
    
    553
    +   * Load or init our settings, and register observers.
    
    554
    +   */
    
    423 555
       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 == "";
    
    466
    -        },
    
    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 = "";
    
    481
    -        },
    
    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;
    
    490
    -        },
    
    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;
    
    503
    -        },
    
    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
    -    });
    
    556
    +    if (this.#initialized) {
    
    557
    +      lazy.logger.warn("Called init twice.");
    
    558
    +      return;
    
    559
    +    }
    
    560
    +    try {
    
    561
    +      await this.#initInternal();
    
    562
    +      this.#initialized = true;
    
    563
    +      this.#initComplete();
    
    564
    +    } catch (e) {
    
    565
    +      this.#initFailed(e);
    
    566
    +      throw e;
    
    567
    +    }
    
    568
    +  }
    
    569
    +
    
    570
    +  /**
    
    571
    +   * The actual implementation of the initialization, which is wrapped to make
    
    572
    +   * it easier to update initializatedPromise.
    
    573
    +   */
    
    574
    +  async #initInternal() {
    
    575
    +    try {
    
    576
    +      const req = await fetch("chrome://global/content/pt_config.json");
    
    577
    +      const config = await req.json();
    
    578
    +      lazy.logger.debug("Loaded pt_config.json", config);
    
    579
    +      this.#recommendedPT = config.recommendedDefault;
    
    580
    +      this.#builtinBridges = config.bridges;
    
    581
    +    } catch (e) {
    
    582
    +      lazy.logger.error("Could not load the built-in PT config.", e);
    
    583
    +    }
    
    551 584
     
    
    552 585
         // TODO: We could use a shared promise, and wait for it to be fullfilled
    
    553 586
         // instead of Service.obs.
    
    ... ... @@ -557,16 +590,18 @@ export const TorSettings = {
    557 590
             // Do not want notifications for initially loaded prefs.
    
    558 591
             this.freezeNotifications();
    
    559 592
             try {
    
    560
    -          this.loadFromPrefs();
    
    593
    +          this.#allowUninitialized = true;
    
    594
    +          this.#loadFromPrefs();
    
    561 595
             } finally {
    
    562
    -          this._notificationQueue.clear();
    
    596
    +          this.#allowUninitialized = false;
    
    597
    +          this.#notificationQueue.clear();
    
    563 598
               this.thawNotifications();
    
    564 599
             }
    
    565 600
           }
    
    566 601
           try {
    
    567 602
             const provider = await lazy.TorProviderBuilder.build();
    
    568 603
             if (provider.isRunning) {
    
    569
    -          this.handleProcessReady();
    
    604
    +          this.#handleProcessReady();
    
    570 605
               // No need to add an observer to call this again.
    
    571 606
               return;
    
    572 607
             }
    
    ... ... @@ -574,9 +609,33 @@ export const TorSettings = {
    574 609
     
    
    575 610
           Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
    
    576 611
         }
    
    577
    -  },
    
    612
    +  }
    
    613
    +
    
    614
    +  /**
    
    615
    +   * Check whether the object has been successfully initialized, and throw if
    
    616
    +   * it has not.
    
    617
    +   */
    
    618
    +  #checkIfInitialized() {
    
    619
    +    if (!this.#initialized) {
    
    620
    +      lazy.logger.trace("Not initialized code path.");
    
    621
    +      throw new Error(
    
    622
    +        "TorSettings has not been initialized yet, or its initialization failed"
    
    623
    +      );
    
    624
    +    }
    
    625
    +  }
    
    626
    +
    
    627
    +  /**
    
    628
    +   * Tell whether TorSettings has been successfully initialized.
    
    629
    +   *
    
    630
    +   * @returns {boolean}
    
    631
    +   */
    
    632
    +  get initialized() {
    
    633
    +    return this.#initialized;
    
    634
    +  }
    
    578 635
     
    
    579
    -  /* wait for relevant life-cycle events to apply saved settings */
    
    636
    +  /**
    
    637
    +   * Wait for relevant life-cycle events to apply saved settings.
    
    638
    +   */
    
    580 639
       async observe(subject, topic, data) {
    
    581 640
         lazy.logger.debug(`Observed ${topic}`);
    
    582 641
     
    
    ... ... @@ -586,21 +645,26 @@ export const TorSettings = {
    586 645
               this,
    
    587 646
               lazy.TorProviderTopics.ProcessIsReady
    
    588 647
             );
    
    589
    -        await this.handleProcessReady();
    
    648
    +        await this.#handleProcessReady();
    
    590 649
             break;
    
    591 650
         }
    
    592
    -  },
    
    651
    +  }
    
    593 652
     
    
    594
    -  // once the tor daemon is ready, we need to apply our settings
    
    595
    -  async handleProcessReady() {
    
    653
    +  /**
    
    654
    +   * Apply the settings once the tor provider is ready and notify any observer
    
    655
    +   * that the settings can be used.
    
    656
    +   */
    
    657
    +  async #handleProcessReady() {
    
    596 658
         // push down settings to tor
    
    597
    -    await this.applySettings();
    
    659
    +    await this.#applySettings(true);
    
    598 660
         lazy.logger.info("Ready");
    
    599 661
         Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
    
    600
    -  },
    
    662
    +  }
    
    601 663
     
    
    602
    -  // load our settings from prefs
    
    603
    -  loadFromPrefs() {
    
    664
    +  /**
    
    665
    +   * Load our settings from prefs.
    
    666
    +   */
    
    667
    +  #loadFromPrefs() {
    
    604 668
         lazy.logger.debug("loadFromPrefs()");
    
    605 669
     
    
    606 670
         /* Quickstart */
    
    ... ... @@ -671,12 +735,16 @@ export const TorSettings = {
    671 735
             ""
    
    672 736
           );
    
    673 737
         }
    
    674
    -  },
    
    738
    +  }
    
    675 739
     
    
    676
    -  // save our settings to prefs
    
    740
    +  /**
    
    741
    +   * Save our settings to prefs.
    
    742
    +   */
    
    677 743
       saveToPrefs() {
    
    678 744
         lazy.logger.debug("saveToPrefs()");
    
    679 745
     
    
    746
    +    this.#checkIfInitialized();
    
    747
    +
    
    680 748
         /* Quickstart */
    
    681 749
         Services.prefs.setBoolPref(
    
    682 750
           TorSettingsPrefs.quickstart.enabled,
    
    ... ... @@ -758,77 +826,100 @@ export const TorSettings = {
    758 826
         Services.prefs.setBoolPref(TorSettingsPrefs.enabled, true);
    
    759 827
     
    
    760 828
         return this;
    
    761
    -  },
    
    829
    +  }
    
    762 830
     
    
    763
    -  // push our settings down to the tor daemon
    
    831
    +  /**
    
    832
    +   * Push our settings down to the tor provider.
    
    833
    +   */
    
    764 834
       async applySettings() {
    
    765
    -    lazy.logger.debug("applySettings()");
    
    835
    +    this.#checkIfInitialized();
    
    836
    +    return this.#applySettings(false);
    
    837
    +  }
    
    838
    +
    
    839
    +  /**
    
    840
    +   * Internal implementation of applySettings that does not check if we are
    
    841
    +   * initialized.
    
    842
    +   */
    
    843
    +  async #applySettings(allowUninitialized) {
    
    844
    +    lazy.logger.debug("#applySettings()");
    
    845
    +
    
    766 846
         const settingsMap = new Map();
    
    767 847
     
    
    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
    -    }
    
    848
    +    // #applySettings can be called only when #allowUninitialized is false
    
    849
    +    this.#allowUninitialized = allowUninitialized;
    
    777 850
     
    
    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;
    
    851
    +    try {
    
    852
    +      /* Bridges */
    
    853
    +      const haveBridges =
    
    854
    +        this.bridges.enabled && !!this.bridges.bridge_strings.length;
    
    855
    +      settingsMap.set(TorConfigKeys.useBridges, haveBridges);
    
    856
    +      if (haveBridges) {
    
    857
    +        settingsMap.set(TorConfigKeys.bridgeList, this.bridges.bridge_strings);
    
    858
    +      } else {
    
    859
    +        settingsMap.set(TorConfigKeys.bridgeList, null);
    
    807 860
           }
    
    808
    -    }
    
    809 861
     
    
    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);
    
    862
    +      /* Proxy */
    
    863
    +      settingsMap.set(TorConfigKeys.socks4Proxy, null);
    
    864
    +      settingsMap.set(TorConfigKeys.socks5Proxy, null);
    
    865
    +      settingsMap.set(TorConfigKeys.socks5ProxyUsername, null);
    
    866
    +      settingsMap.set(TorConfigKeys.socks5ProxyPassword, null);
    
    867
    +      settingsMap.set(TorConfigKeys.httpsProxy, null);
    
    868
    +      settingsMap.set(TorConfigKeys.httpsProxyAuthenticator, null);
    
    869
    +      if (this.proxy.enabled) {
    
    870
    +        const address = this.proxy.address;
    
    871
    +        const port = this.proxy.port;
    
    872
    +        const username = this.proxy.username;
    
    873
    +        const password = this.proxy.password;
    
    874
    +
    
    875
    +        switch (this.proxy.type) {
    
    876
    +          case TorProxyType.Socks4:
    
    877
    +            settingsMap.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
    
    878
    +            break;
    
    879
    +          case TorProxyType.Socks5:
    
    880
    +            settingsMap.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
    
    881
    +            settingsMap.set(TorConfigKeys.socks5ProxyUsername, username);
    
    882
    +            settingsMap.set(TorConfigKeys.socks5ProxyPassword, password);
    
    883
    +            break;
    
    884
    +          case TorProxyType.HTTPS:
    
    885
    +            settingsMap.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
    
    886
    +            settingsMap.set(
    
    887
    +              TorConfigKeys.httpsProxyAuthenticator,
    
    888
    +              `${username}:${password}`
    
    889
    +            );
    
    890
    +            break;
    
    891
    +        }
    
    892
    +      }
    
    893
    +
    
    894
    +      /* Firewall */
    
    895
    +      if (this.firewall.enabled) {
    
    896
    +        const reachableAddresses = this.firewall.allowed_ports
    
    897
    +          .map(port => `*:${port}`)
    
    898
    +          .join(",");
    
    899
    +        settingsMap.set(TorConfigKeys.reachableAddresses, reachableAddresses);
    
    900
    +      } else {
    
    901
    +        settingsMap.set(TorConfigKeys.reachableAddresses, null);
    
    902
    +      }
    
    903
    +    } finally {
    
    904
    +      this.#allowUninitialized = false;
    
    818 905
         }
    
    819 906
     
    
    820 907
         /* Push to Tor */
    
    821 908
         const provider = await lazy.TorProviderBuilder.build();
    
    822 909
         await provider.writeSettings(settingsMap);
    
    910
    +  }
    
    823 911
     
    
    824
    -    return this;
    
    825
    -  },
    
    826
    -
    
    827
    -  // set all of our settings at once from a settings object
    
    912
    +  /**
    
    913
    +   * Set all of our settings at once from a settings object.
    
    914
    +   *
    
    915
    +   * @param {object} settings The settings object to set
    
    916
    +   */
    
    828 917
       setSettings(settings) {
    
    829 918
         lazy.logger.debug("setSettings()");
    
    919
    +    this.#checkIfInitialized();
    
    920
    +
    
    830 921
         const backup = this.getSettings();
    
    831
    -    const backup_notifications = [...this._notificationQueue];
    
    922
    +    const backupNotifications = [...this.#notificationQueue];
    
    832 923
     
    
    833 924
         // Hold off on lots of notifications until all settings are changed.
    
    834 925
         this.freezeNotifications();
    
    ... ... @@ -869,10 +960,10 @@ export const TorSettings = {
    869 960
           // some other call to TorSettings to change anything whilst we are
    
    870 961
           // in this context (other than lower down in this call stack), so it is
    
    871 962
           // 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);
    
    963
    +      this.#settings = backup;
    
    964
    +      this.#notificationQueue.clear();
    
    965
    +      for (const notification of backupNotifications) {
    
    966
    +        this.#notificationQueue.add(notification);
    
    876 967
           }
    
    877 968
     
    
    878 969
           lazy.logger.error("setSettings failed", ex);
    
    ... ... @@ -880,12 +971,36 @@ export const TorSettings = {
    880 971
           this.thawNotifications();
    
    881 972
         }
    
    882 973
     
    
    883
    -    lazy.logger.debug("setSettings result", this._settings);
    
    884
    -  },
    
    974
    +    lazy.logger.debug("setSettings result", this.#settings);
    
    975
    +  }
    
    885 976
     
    
    886
    -  // get a copy of all our settings
    
    977
    +  /**
    
    978
    +   * Get a copy of all our settings.
    
    979
    +   *
    
    980
    +   * @returns {object} A copy of the settings object
    
    981
    +   */
    
    887 982
       getSettings() {
    
    888 983
         lazy.logger.debug("getSettings()");
    
    889
    -    return structuredClone(this._settings);
    
    890
    -  },
    
    891
    -};
    984
    +    this.#checkIfInitialized();
    
    985
    +    return structuredClone(this.#settings);
    
    986
    +  }
    
    987
    +
    
    988
    +  /**
    
    989
    +   * Return an array with the pluggable transports for which we have at least a
    
    990
    +   * built-in bridge line.
    
    991
    +   *
    
    992
    +   * @returns {string[]} An array with PT identifiers
    
    993
    +   */
    
    994
    +  get builtinBridgeTypes() {
    
    995
    +    this.#checkIfInitialized();
    
    996
    +    const types = Object.keys(this.#builtinBridges);
    
    997
    +    const recommendedIndex = types.indexOf(this.#recommendedPT);
    
    998
    +    if (recommendedIndex > 0) {
    
    999
    +      types.splice(recommendedIndex, 1);
    
    1000
    +      types.unshift(this.#recommendedPT);
    
    1001
    +    }
    
    1002
    +    return types;
    
    1003
    +  }
    
    1004
    +}
    
    1005
    +
    
    1006
    +export const TorSettings = new TorSettingsImpl();

  • tools/torbrowser/bridges.js deleted
    1
    -pref("extensions.torlauncher.default_bridge_recommended_type", "obfs4");
    
    2
    -
    
    3
    -// Default bridges.
    
    4
    -pref(
    
    5
    -  "extensions.torlauncher.default_bridge.obfs4.1",
    
    6
    -  "obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1"
    
    7
    -);
    
    8
    -pref(
    
    9
    -  "extensions.torlauncher.default_bridge.obfs4.2",
    
    10
    -  "obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0"
    
    11
    -);
    
    12
    -pref(
    
    13
    -  "extensions.torlauncher.default_bridge.obfs4.3",
    
    14
    -  "obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0"
    
    15
    -);
    
    16
    -pref(
    
    17
    -  "extensions.torlauncher.default_bridge.obfs4.4",
    
    18
    -  "obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0"
    
    19
    -);
    
    20
    -pref(
    
    21
    -  "extensions.torlauncher.default_bridge.obfs4.5",
    
    22
    -  "obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0"
    
    23
    -);
    
    24
    -pref(
    
    25
    -  "extensions.torlauncher.default_bridge.obfs4.6",
    
    26
    -  "obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0"
    
    27
    -);
    
    28
    -pref(
    
    29
    -  "extensions.torlauncher.default_bridge.obfs4.7",
    
    30
    -  "obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0"
    
    31
    -);
    
    32
    -pref(
    
    33
    -  "extensions.torlauncher.default_bridge.obfs4.8",
    
    34
    -  "obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0"
    
    35
    -);
    
    36
    -pref(
    
    37
    -  "extensions.torlauncher.default_bridge.obfs4.9",
    
    38
    -  "obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0"
    
    39
    -);
    
    40
    -pref(
    
    41
    -  "extensions.torlauncher.default_bridge.obfs4.10",
    
    42
    -  "obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0"
    
    43
    -);
    
    44
    -pref(
    
    45
    -  "extensions.torlauncher.default_bridge.obfs4.11",
    
    46
    -  "obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0"
    
    47
    -);
    
    48
    -
    
    49
    -pref(
    
    50
    -  "extensions.torlauncher.default_bridge.meek-azure.1",
    
    51
    -  "meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com"
    
    52
    -);
    
    53
    -
    
    54
    -pref(
    
    55
    -  "extensions.torlauncher.default_bridge.snowflake.1",
    
    56
    -  "snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"
    
    57
    -);
    
    58
    -
    
    59
    -pref(
    
    60
    -  "extensions.torlauncher.default_bridge.snowflake.2",
    
    61
    -  "snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"
    
    62
    -);

  • tools/torbrowser/deploy.sh
    ... ... @@ -6,17 +6,10 @@ BUILD_OUTPUT="$2"
    6 6
     SCRIPT_DIR="$(realpath "$(dirname "$0")")"
    
    7 7
     
    
    8 8
     RESDIR="$BUILD_OUTPUT/dist/firefox"
    
    9
    -if [ "$(uname)" = "Darwin" ]; then 
    
    9
    +if [ "$(uname)" = "Darwin" ]; then
    
    10 10
         RESDIR="$RESDIR/Tor Browser.app/Contents/Resources"
    
    11 11
     fi
    
    12 12
     
    
    13
    -# Add built-in bridges
    
    14
    -mkdir -p "$BUILD_OUTPUT/_omni/defaults/preferences"
    
    15
    -cat "$BUILD_OUTPUT/dist/bin/browser/defaults/preferences/000-tor-browser.js" "$SCRIPT_DIR/bridges.js" >> "$BUILD_OUTPUT/_omni/defaults/preferences/000-tor-browser.js"
    
    16
    -cd "$BUILD_OUTPUT/_omni"
    
    17
    -zip -Xmr "$RESDIR/browser/omni.ja" "defaults/preferences/000-tor-browser.js"
    
    18
    -rm -rf "$BUILD_OUTPUT/_omni"
    
    19
    -
    
    20 13
     # Repackage the manual
    
    21 14
     # rm -rf $BUILD_OUTPUT/_omni
    
    22 15
     # mkdir $BUILD_OUTPUT/_omni
    
    ... ... @@ -34,12 +27,12 @@ if [ "$(uname)" = "Darwin" ]; then
    34 27
         cd "$BINARIES/Tor Browser.app/Contents/MacOS"
    
    35 28
         "$SCRIPT_DIR/browser-self-sign-macos.sh"
    
    36 29
     
    
    37
    -  else
    
    30
    +else
    
    38 31
     
    
    39 32
         # backup the startup script
    
    40 33
         mv "$BINARIES/dev/Browser/firefox" "$BINARIES/dev/Browser/firefox.bak"
    
    41
    -    
    
    42
    -    # copy binaries 
    
    34
    +
    
    35
    +    # copy binaries
    
    43 36
         cp -r "$RESDIR/"* "$BINARIES/dev/Browser"
    
    44 37
         rm -rf "$BINARIES/dev/Browser/TorBrowser/Data/Browser/profile.default/startupCache"
    
    45 38