| ... | 
... | 
@@ -301,8 +301,12 @@ class TorSettingsImpl { | 
| 
301
 | 
301
 | 
           return this.#parsePort(val, false) ?? 0;
  | 
| 
302
 | 
302
 | 
         },
  | 
| 
303
 | 
303
 | 
       },
  | 
| 
304
 | 
 
 | 
-      username: {},
 | 
| 
305
 | 
 
 | 
-      password: {},
 | 
| 
 
 | 
304
 | 
+      username: {
 | 
| 
 
 | 
305
 | 
+        transform: val => val ?? "",
  | 
| 
 
 | 
306
 | 
+      },
  | 
| 
 
 | 
307
 | 
+      password: {
 | 
| 
 
 | 
308
 | 
+        transform: val => val ?? "",
  | 
| 
 
 | 
309
 | 
+      },
  | 
| 
306
 | 
310
 | 
       uri: {
 | 
| 
307
 | 
311
 | 
         getter: () => {
 | 
| 
308
 | 
312
 | 
           const { type, address, port, username, password } = this.proxy;
 | 
| ... | 
... | 
@@ -910,7 +914,11 @@ class TorSettingsImpl { | 
| 
910
 | 
914
 | 
   }
  | 
| 
911
 | 
915
 | 
 
  | 
| 
912
 | 
916
 | 
   /**
  | 
| 
913
 | 
 
 | 
-   * Set all of our settings at once from a settings object.
  | 
| 
 
 | 
917
 | 
+   * Set blocks of settings at once from an object.
  | 
| 
 
 | 
918
 | 
+   *
  | 
| 
 
 | 
919
 | 
+   * It is possible to set all settings, or only some sections (e.g., only
  | 
| 
 
 | 
920
 | 
+   * bridges), but if a key is present, its settings must make sense (e.g., if
  | 
| 
 
 | 
921
 | 
+   * bridges are enabled, a valid source must be provided).
  | 
| 
914
 | 
922
 | 
    *
  | 
| 
915
 | 
923
 | 
    * @param {object} settings The settings object to set
 | 
| 
916
 | 
924
 | 
    */
  | 
| ... | 
... | 
@@ -924,35 +932,59 @@ class TorSettingsImpl { | 
| 
924
 | 
932
 | 
     // Hold off on lots of notifications until all settings are changed.
  | 
| 
925
 | 
933
 | 
     this.freezeNotifications();
  | 
| 
926
 | 
934
 | 
     try {
 | 
| 
927
 | 
 
 | 
-      this.bridges.enabled = !!settings.bridges.enabled;
  | 
| 
928
 | 
 
 | 
-      this.bridges.source = settings.bridges.source;
  | 
| 
929
 | 
 
 | 
-      switch (settings.bridges.source) {
 | 
| 
930
 | 
 
 | 
-        case TorBridgeSource.BridgeDB:
  | 
| 
931
 | 
 
 | 
-        case TorBridgeSource.UserProvided:
  | 
| 
932
 | 
 
 | 
-          this.bridges.bridge_strings = settings.bridges.bridge_strings;
  | 
| 
933
 | 
 
 | 
-          break;
  | 
| 
934
 | 
 
 | 
-        case TorBridgeSource.BuiltIn: {
 | 
| 
935
 | 
 
 | 
-          this.bridges.builtin_type = settings.bridges.builtin_type;
  | 
| 
936
 | 
 
 | 
-          if (!this.bridges.bridge_strings.length) {
 | 
| 
937
 | 
 
 | 
-            // No bridges were found when setting the builtin_type.
  | 
| 
938
 | 
 
 | 
-            throw new Error(
  | 
| 
939
 | 
 
 | 
-              `No available builtin bridges of type ${settings.bridges.builtin_type}`
 | 
| 
940
 | 
 
 | 
-            );
  | 
| 
941
 | 
 
 | 
-          }
  | 
| 
942
 | 
 
 | 
-          break;
  | 
| 
 
 | 
935
 | 
+      if ("bridges" in settings) {
 | 
| 
 
 | 
936
 | 
+        this.bridges.enabled = !!settings.bridges.enabled;
  | 
| 
 
 | 
937
 | 
+        // Currently, disabling bridges in the UI does not remove the lines,
  | 
| 
 
 | 
938
 | 
+        // because we call only the `enabled` setter.
  | 
| 
 
 | 
939
 | 
+        // So, if the bridge source is undefined but bridges are disabled,
  | 
| 
 
 | 
940
 | 
+        // do not force Invalid. Instead, keep the current source.
  | 
| 
 
 | 
941
 | 
+        if (this.bridges.enabled || settings.bridges.source !== undefined) {
 | 
| 
 
 | 
942
 | 
+          this.bridges.source = settings.bridges.source;
  | 
| 
943
 | 
943
 | 
         }
  | 
| 
944
 | 
 
 | 
-        case TorBridgeSource.Invalid:
  | 
| 
945
 | 
 
 | 
-          break;
  | 
| 
946
 | 
 
 | 
-        default:
  | 
| 
947
 | 
 
 | 
-          if (settings.bridges.enabled) {
 | 
| 
948
 | 
 
 | 
-            throw new Error(
  | 
| 
949
 | 
 
 | 
-              `Bridge source '${settings.source}' is not a valid source`
 | 
| 
950
 | 
 
 | 
-            );
  | 
| 
 
 | 
944
 | 
+        switch (settings.bridges.source) {
 | 
| 
 
 | 
945
 | 
+          case TorBridgeSource.BridgeDB:
  | 
| 
 
 | 
946
 | 
+          case TorBridgeSource.UserProvided:
  | 
| 
 
 | 
947
 | 
+            this.bridges.bridge_strings = settings.bridges.bridge_strings;
  | 
| 
 
 | 
948
 | 
+            break;
  | 
| 
 
 | 
949
 | 
+          case TorBridgeSource.BuiltIn: {
 | 
| 
 
 | 
950
 | 
+            this.bridges.builtin_type = settings.bridges.builtin_type;
  | 
| 
 
 | 
951
 | 
+            if (!this.bridges.bridge_strings.length) {
 | 
| 
 
 | 
952
 | 
+              // No bridges were found when setting the builtin_type.
  | 
| 
 
 | 
953
 | 
+              throw new Error(
  | 
| 
 
 | 
954
 | 
+                `No available builtin bridges of type ${settings.bridges.builtin_type}`
 | 
| 
 
 | 
955
 | 
+              );
  | 
| 
 
 | 
956
 | 
+            }
  | 
| 
 
 | 
957
 | 
+            break;
  | 
| 
951
 | 
958
 | 
           }
  | 
| 
952
 | 
 
 | 
-          break;
  | 
| 
 
 | 
959
 | 
+          case TorBridgeSource.Invalid:
  | 
| 
 
 | 
960
 | 
+            break;
  | 
| 
 
 | 
961
 | 
+          default:
  | 
| 
 
 | 
962
 | 
+            if (settings.bridges.enabled) {
 | 
| 
 
 | 
963
 | 
+              throw new Error(
  | 
| 
 
 | 
964
 | 
+                `Bridge source '${settings.source}' is not a valid source`
 | 
| 
 
 | 
965
 | 
+              );
  | 
| 
 
 | 
966
 | 
+            }
  | 
| 
 
 | 
967
 | 
+            break;
  | 
| 
 
 | 
968
 | 
+        }
  | 
| 
953
 | 
969
 | 
       }
  | 
| 
954
 | 
970
 | 
 
  | 
| 
955
 | 
 
 | 
-      // TODO: proxy and firewall
  | 
| 
 
 | 
971
 | 
+      if ("proxy" in settings) {
 | 
| 
 
 | 
972
 | 
+        this.proxy.enabled = !!settings.proxy.enabled;
  | 
| 
 
 | 
973
 | 
+        if (this.proxy.enabled) {
 | 
| 
 
 | 
974
 | 
+          this.proxy.type = settings.proxy.type;
  | 
| 
 
 | 
975
 | 
+          this.proxy.address = settings.proxy.address;
  | 
| 
 
 | 
976
 | 
+          this.proxy.port = settings.proxy.port;
  | 
| 
 
 | 
977
 | 
+          this.proxy.username = settings.proxy.username;
  | 
| 
 
 | 
978
 | 
+          this.proxy.password = settings.proxy.password;
  | 
| 
 
 | 
979
 | 
+        }
  | 
| 
 
 | 
980
 | 
+      }
  | 
| 
 
 | 
981
 | 
+
  | 
| 
 
 | 
982
 | 
+      if ("firewall" in settings) {
 | 
| 
 
 | 
983
 | 
+        this.firewall.enabled = !!settings.firewall.enabled;
  | 
| 
 
 | 
984
 | 
+        if (this.firewall.enabled) {
 | 
| 
 
 | 
985
 | 
+          this.firewall.allowed_ports = settings.firewall.allowed_ports;
  | 
| 
 
 | 
986
 | 
+        }
  | 
| 
 
 | 
987
 | 
+      }
  | 
| 
956
 | 
988
 | 
     } catch (ex) {
 | 
| 
957
 | 
989
 | 
       // Restore the old settings without any new notifications generated from
  | 
| 
958
 | 
990
 | 
       // the above code.
  |