diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json
index 8a14f8bf..63bfd48c 100644
--- a/desktop/src/onionshare/resources/locale/en.json
+++ b/desktop/src/onionshare/resources/locale/en.json
@@ -62,14 +62,14 @@
"gui_settings_password_label": "Password",
"gui_settings_tor_bridges": "Connect using a Tor bridge?",
"gui_settings_tor_bridges_label": "Bridges help you access the Tor Network in places where Tor is blocked. Depending on where you are, one bridge may work better than another.",
+ "gui_settings_bridge_use_checkbox": "Use a bridge",
+ "gui_settings_bridge_radio_builtin": "Select a built-in bridge",
"gui_settings_bridge_none_radio_option": "Don't use a bridge",
- "gui_settings_bridge_obfs4_radio_option": "Use built-in obfs4 bridge",
- "gui_settings_bridge_meek_azure_radio_option": "Use built-in meek-azure bridge",
- "gui_settings_bridge_snowflake_radio_option": "Use built-in snowflake bridge",
"gui_settings_meek_lite_expensive_warning": "Warning: The meek-azure bridges are very costly for the Tor Project to run.
Only use them if unable to connect to Tor directly, via obfs4 transports, or other normal bridges.",
"gui_settings_bridge_moat_radio_option": "Request a bridge from torproject.org",
"gui_settings_bridge_moat_button": "Request a New Bridge...",
"gui_settings_bridge_custom_radio_option": "Provide a bridge you learned about from a trusted source",
+ "gui_settings_bridge_custom_placeholder": "type address:port (one per line)",
"gui_settings_tor_bridges_invalid": "None of the bridges you added work.\nDouble-check them or add others.",
"gui_settings_button_save": "Save",
"gui_settings_button_cancel": "Cancel",
diff --git a/desktop/src/onionshare/tor_settings_dialog.py b/desktop/src/onionshare/tor_settings_dialog.py
index 477758c2..6fa4dda1 100644
--- a/desktop/src/onionshare/tor_settings_dialog.py
+++ b/desktop/src/onionshare/tor_settings_dialog.py
@@ -72,15 +72,6 @@ class TorSettingsDialog(QtWidgets.QDialog):
# Bridge options for bundled tor
- bridges_label = QtWidgets.QLabel(strings._("gui_settings_tor_bridges_label"))
- bridges_label.setWordWrap(True)
-
- # No bridges option radio
- self.bridge_none_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_bridge_none_radio_option")
- )
- self.bridge_none_radio.toggled.connect(self.bridge_none_radio_toggled)
-
(
self.tor_path,
self.tor_geo_ip_file_path,
@@ -89,53 +80,30 @@ class TorSettingsDialog(QtWidgets.QDialog):
self.snowflake_file_path,
) = self.common.gui.get_tor_paths()
- # obfs4 option radio
- # if the obfs4proxy binary is missing, we can't use obfs4 transports
- self.bridge_obfs4_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_bridge_obfs4_radio_option")
- )
- self.bridge_obfs4_radio.toggled.connect(self.bridge_obfs4_radio_toggled)
- if not self.obfs4proxy_file_path or not os.path.isfile(
- self.obfs4proxy_file_path
- ):
- self.common.log(
- "TorSettingsDialog",
- "__init__",
- f"missing binary {self.obfs4proxy_file_path}, hiding obfs4 bridge",
- )
- self.bridge_obfs4_radio.hide()
+ bridges_label = QtWidgets.QLabel(strings._("gui_settings_tor_bridges_label"))
+ bridges_label.setWordWrap(True)
- # meek-azure option radio
- # if the obfs4proxy binary is missing, we can't use meek_lite-azure transports
- self.bridge_meek_azure_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_bridge_meek_azure_radio_option")
+ self.bridge_use_checkbox = QtWidgets.QCheckBox(
+ strings._("gui_settings_bridge_use_checkbox")
)
- self.bridge_meek_azure_radio.toggled.connect(
- self.bridge_meek_azure_radio_toggled
+ self.bridge_use_checkbox.stateChanged.connect(
+ self.bridge_use_checkbox_state_changed
)
- if not self.obfs4proxy_file_path or not os.path.isfile(
- self.obfs4proxy_file_path
- ):
- self.common.log(
- "TorSettingsDialog",
- "__init__",
- f"missing binary {self.obfs4proxy_file_path}, hiding meek-azure bridge",
- )
- self.bridge_meek_azure_radio.hide()
- # snowflake option radio
- # if the snowflake-client binary is missing, we can't use snowflake transports
- self.bridge_snowflake_radio = QtWidgets.QRadioButton(
- strings._("gui_settings_bridge_snowflake_radio_option")
+ # Built-in bridge
+ self.bridge_builtin_radio = QtWidgets.QRadioButton(
+ strings._("gui_settings_bridge_radio_builtin")
)
- self.bridge_snowflake_radio.toggled.connect(self.bridge_snowflake_radio_toggled)
- if not self.snowflake_file_path or not os.path.isfile(self.snowflake_file_path):
- self.common.log(
- "TorSettingsDialog",
- "__init__",
- f"missing binary {self.snowflake_file_path}, hiding snowflake bridge",
- )
- self.bridge_snowflake_radio.hide()
+ self.bridge_builtin_radio.toggled.connect(self.bridge_builtin_radio_toggled)
+ self.bridge_builtin_dropdown = QtWidgets.QComboBox()
+ self.bridge_builtin_dropdown.currentTextChanged.connect(
+ self.bridge_builtin_dropdown_changed
+ )
+ if self.obfs4proxy_file_path and os.path.isfile(self.obfs4proxy_file_path):
+ self.bridge_builtin_dropdown.addItem("obfs4")
+ self.bridge_builtin_dropdown.addItem("meek-azure")
+ if self.snowflake_file_path and os.path.isfile(self.snowflake_file_path):
+ self.bridge_builtin_dropdown.addItem("snowflake")
# Request a bridge from torproject.org (moat)
self.bridge_moat_radio = QtWidgets.QRadioButton(
@@ -147,6 +115,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
)
self.bridge_moat_button.clicked.connect(self.bridge_moat_button_clicked)
self.bridge_moat_textbox = QtWidgets.QPlainTextEdit()
+ self.bridge_moat_textbox.setMinimumHeight(100)
self.bridge_moat_textbox.setMaximumHeight(100)
self.bridge_moat_textbox.setReadOnly(True)
self.bridge_moat_textbox.setWordWrapMode(QtGui.QTextOption.NoWrap)
@@ -154,7 +123,6 @@ class TorSettingsDialog(QtWidgets.QDialog):
bridge_moat_textbox_options_layout.addWidget(self.bridge_moat_button)
bridge_moat_textbox_options_layout.addWidget(self.bridge_moat_textbox)
self.bridge_moat_textbox_options = QtWidgets.QWidget()
- self.bridge_moat_textbox_options.setMinimumHeight(50)
self.bridge_moat_textbox_options.setLayout(bridge_moat_textbox_options_layout)
self.bridge_moat_textbox_options.hide()
@@ -164,8 +132,11 @@ class TorSettingsDialog(QtWidgets.QDialog):
)
self.bridge_custom_radio.toggled.connect(self.bridge_custom_radio_toggled)
self.bridge_custom_textbox = QtWidgets.QPlainTextEdit()
- self.bridge_custom_textbox.setMaximumHeight(200)
- self.bridge_custom_textbox.setPlaceholderText("[address:port] [identifier]")
+ self.bridge_custom_textbox.setMinimumHeight(100)
+ self.bridge_custom_textbox.setMaximumHeight(100)
+ self.bridge_custom_textbox.setPlaceholderText(
+ strings._("gui_settings_bridge_custom_placeholder")
+ )
bridge_custom_textbox_options_layout = QtWidgets.QVBoxLayout()
bridge_custom_textbox_options_layout.addWidget(self.bridge_custom_textbox)
@@ -176,17 +147,22 @@ class TorSettingsDialog(QtWidgets.QDialog):
)
self.bridge_custom_textbox_options.hide()
+ # Bridge settings layout
+ bridge_settings_layout = QtWidgets.QVBoxLayout()
+ bridge_settings_layout.addWidget(self.bridge_builtin_radio)
+ bridge_settings_layout.addWidget(self.bridge_builtin_dropdown)
+ bridge_settings_layout.addWidget(self.bridge_moat_radio)
+ bridge_settings_layout.addWidget(self.bridge_moat_textbox_options)
+ bridge_settings_layout.addWidget(self.bridge_custom_radio)
+ bridge_settings_layout.addWidget(self.bridge_custom_textbox_options)
+ self.bridge_settings = QtWidgets.QWidget()
+ self.bridge_settings.setLayout(bridge_settings_layout)
+
# Bridges layout/widget
bridges_layout = QtWidgets.QVBoxLayout()
bridges_layout.addWidget(bridges_label)
- bridges_layout.addWidget(self.bridge_none_radio)
- bridges_layout.addWidget(self.bridge_obfs4_radio)
- bridges_layout.addWidget(self.bridge_meek_azure_radio)
- bridges_layout.addWidget(self.bridge_snowflake_radio)
- bridges_layout.addWidget(self.bridge_moat_radio)
- bridges_layout.addWidget(self.bridge_moat_textbox_options)
- bridges_layout.addWidget(self.bridge_custom_radio)
- bridges_layout.addWidget(self.bridge_custom_textbox_options)
+ bridges_layout.addWidget(self.bridge_use_checkbox)
+ bridges_layout.addWidget(self.bridge_settings)
self.bridges = QtWidgets.QWidget()
self.bridges.setLayout(bridges_layout)
@@ -412,46 +388,56 @@ class TorSettingsDialog(QtWidgets.QDialog):
)
if self.old_settings.get("no_bridges"):
- self.bridge_none_radio.setChecked(True)
- self.bridge_obfs4_radio.setChecked(False)
- self.bridge_meek_azure_radio.setChecked(False)
- self.bridge_snowflake_radio.setChecked(False)
- self.bridge_moat_radio.setChecked(False)
- self.bridge_custom_radio.setChecked(False)
- else:
- self.bridge_none_radio.setChecked(False)
- self.bridge_obfs4_radio.setChecked(
- self.old_settings.get("tor_bridges_use_obfs4")
- )
- self.bridge_meek_azure_radio.setChecked(
- self.old_settings.get("tor_bridges_use_meek_lite_azure")
- )
- self.bridge_snowflake_radio.setChecked(
- self.old_settings.get("tor_bridges_use_snowflake")
- )
- self.bridge_moat_radio.setChecked(
- self.old_settings.get("tor_bridges_use_moat")
- )
- moat_bridges = self.old_settings.get("tor_bridges_use_moat_bridges")
- self.bridge_moat_textbox.document().setPlainText(moat_bridges)
- if len(moat_bridges.strip()) > 0:
- self.bridge_moat_textbox.show()
- else:
- self.bridge_moat_textbox.hide()
+ self.bridge_use_checkbox.setCheckState(QtCore.Qt.Unchecked)
+ self.bridge_settings.hide()
- if self.old_settings.get("tor_bridges_use_custom_bridges"):
- self.bridge_custom_radio.setChecked(True)
- # Remove the 'Bridge' lines at the start of each bridge.
- # They are added automatically to provide compatibility with
- # copying/pasting bridges provided from https://bridges.torproject.org
- new_bridges = []
- bridges = self.old_settings.get("tor_bridges_use_custom_bridges").split(
- "Bridge "
- )
- for bridge in bridges:
- new_bridges.append(bridge)
- new_bridges = "".join(new_bridges)
- self.bridge_custom_textbox.setPlainText(new_bridges)
+ else:
+ self.bridge_use_checkbox.setCheckState(QtCore.Qt.Checked)
+ self.bridge_settings.show()
+
+ builtin_obfs4 = self.old_settings.get("tor_bridges_use_obfs4")
+ builtin_meek_azure = self.old_settings.get(
+ "tor_bridges_use_meek_lite_azure"
+ )
+ builtin_snowflake = self.old_settings.get("tor_bridges_use_snowflake")
+
+ if builtin_obfs4 or builtin_meek_azure or builtin_snowflake:
+ self.bridge_builtin_radio.setChecked(True)
+ self.bridge_builtin_dropdown.show()
+ if builtin_obfs4:
+ self.bridge_builtin_dropdown.setCurrentText("obfs4")
+ elif builtin_meek_azure:
+ self.bridge_builtin_dropdown.setCurrentText("meek-azure")
+ elif builtin_snowflake:
+ self.bridge_builtin_dropdown.setCurrentText("snowflake")
+
+ self.bridge_moat_textbox_options.hide()
+ self.bridge_custom_textbox_options.hide()
+ else:
+ self.bridge_builtin_radio.setChecked(False)
+ self.bridge_builtin_dropdown.hide()
+
+ use_moat = self.old_settings.get("tor_bridges_use_moat")
+ self.bridge_moat_radio.setChecked(use_moat)
+ if use_moat:
+ self.bridge_builtin_dropdown.hide()
+ self.bridge_custom_textbox_options.hide()
+
+ moat_bridges = self.old_settings.get("tor_bridges_use_moat_bridges")
+ self.bridge_moat_textbox.document().setPlainText(moat_bridges)
+ if len(moat_bridges.strip()) > 0:
+ self.bridge_moat_textbox_options.show()
+ else:
+ self.bridge_moat_textbox_options.hide()
+
+ custom_bridges = self.old_settings.get("tor_bridges_use_custom_bridges")
+ if len(custom_bridges.strip()) != 0:
+ self.bridge_custom_radio.setChecked(True)
+ self.bridge_custom_textbox.setPlainText(custom_bridges)
+
+ self.bridge_builtin_dropdown.hide()
+ self.bridge_moat_textbox_options.hide()
+ self.bridge_custom_textbox_options.show()
def connection_type_bundled_toggled(self, checked):
"""
@@ -463,30 +449,31 @@ class TorSettingsDialog(QtWidgets.QDialog):
self.connection_type_socks.hide()
self.connection_type_bridges_radio_group.show()
- def bridge_none_radio_toggled(self, checked):
+ def bridge_use_checkbox_state_changed(self, state):
"""
- 'No bridges' option was toggled. If checked, enable other bridge options.
+ 'Use a bridge' checkbox changed
+ """
+ if state == QtCore.Qt.Checked:
+ self.bridge_settings.show()
+ self.bridge_builtin_radio.click()
+ self.bridge_builtin_dropdown.setCurrentText("obfs4")
+ else:
+ self.bridge_settings.hide()
+
+ def bridge_builtin_radio_toggled(self, checked):
+ """
+ 'Select a built-in bridge' radio button toggled
"""
if checked:
+ self.bridge_builtin_dropdown.show()
self.bridge_custom_textbox_options.hide()
self.bridge_moat_textbox_options.hide()
- def bridge_obfs4_radio_toggled(self, checked):
+ def bridge_builtin_dropdown_changed(self, selection):
"""
- obfs4 bridges option was toggled. If checked, disable custom bridge options.
+ Build-in bridge selection changed
"""
- if checked:
- self.bridge_custom_textbox_options.hide()
- self.bridge_moat_textbox_options.hide()
-
- def bridge_meek_azure_radio_toggled(self, checked):
- """
- meek_lite_azure bridges option was toggled. If checked, disable custom bridge options.
- """
- if checked:
- self.bridge_custom_textbox_options.hide()
- self.bridge_moat_textbox_options.hide()
-
+ if selection == "meek-azure":
# Alert the user about meek's costliness if it looks like they're turning it on
if not self.old_settings.get("tor_bridges_use_meek_lite_azure"):
Alert(
@@ -495,19 +482,12 @@ class TorSettingsDialog(QtWidgets.QDialog):
QtWidgets.QMessageBox.Warning,
)
- def bridge_snowflake_radio_toggled(self, checked):
- """
- snowflake bridges option was toggled. If checked, disable custom bridge options.
- """
- if checked:
- self.bridge_custom_textbox_options.hide()
- self.bridge_moat_textbox_options.hide()
-
def bridge_moat_radio_toggled(self, checked):
"""
Moat (request bridge) bridges option was toggled. If checked, show moat bridge options.
"""
if checked:
+ self.bridge_builtin_dropdown.hide()
self.bridge_custom_textbox_options.hide()
self.bridge_moat_textbox_options.show()
@@ -534,8 +514,9 @@ class TorSettingsDialog(QtWidgets.QDialog):
Custom bridges option was toggled. If checked, show custom bridge options.
"""
if checked:
- self.bridge_custom_textbox_options.show()
+ self.bridge_builtin_dropdown.hide()
self.bridge_moat_textbox_options.hide()
+ self.bridge_custom_textbox_options.show()
def connection_type_automatic_toggled(self, checked):
"""
@@ -592,6 +573,8 @@ class TorSettingsDialog(QtWidgets.QDialog):
"""
self.common.log("TorSettingsDialog", "test_tor_clicked")
settings = self.settings_from_fields()
+ if not settings:
+ return
onion = Onion(
self.common,
@@ -765,85 +748,77 @@ class TorSettingsDialog(QtWidgets.QDialog):
settings.set("auth_password", self.authenticate_password_extras_password.text())
# Whether we use bridges
- if self.bridge_none_radio.isChecked():
+ if self.bridge_use_checkbox.checkState() == QtCore.Qt.Checked:
+ settings.set("no_bridges", False)
+
+ if self.bridge_builtin_radio.isChecked():
+ selection = self.bridge_builtin_dropdown.currentText()
+ if selection == "obfs4":
+ settings.set("tor_bridges_use_obfs4", True)
+ settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", False)
+ elif selection == "meek-azure":
+ settings.set("tor_bridges_use_obfs4", False)
+ settings.set("tor_bridges_use_meek_lite_azure", True)
+ settings.set("tor_bridges_use_snowflake", False)
+ elif selection == "snowflake":
+ settings.set("tor_bridges_use_obfs4", False)
+ settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", True)
+
+ settings.set("tor_bridges_use_moat", False)
+ settings.set("tor_bridges_use_custom_bridges", "")
+
+ if self.bridge_moat_radio.isChecked():
+ settings.set("tor_bridges_use_obfs4", False)
+ settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", False)
+
+ settings.set("tor_bridges_use_moat", True)
+ settings.set(
+ "tor_bridges_use_moat_bridges",
+ self.bridge_moat_textbox.toPlainText(),
+ )
+
+ settings.set("tor_bridges_use_custom_bridges", "")
+
+ if self.bridge_custom_radio.isChecked():
+ settings.set("tor_bridges_use_obfs4", False)
+ settings.set("tor_bridges_use_meek_lite_azure", False)
+ settings.set("tor_bridges_use_snowflake", False)
+ settings.set("tor_bridges_use_moat", False)
+
+ new_bridges = []
+ bridges = self.bridge_custom_textbox.toPlainText().split("\n")
+ bridges_valid = False
+ for bridge in bridges:
+ if bridge != "":
+ # Check the syntax of the custom bridge to make sure it looks legitimate
+ ipv4_pattern = re.compile(
+ "(obfs4\s+)?(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):([0-9]+)(\s+)([A-Z0-9]+)(.+)$"
+ )
+ ipv6_pattern = re.compile(
+ "(obfs4\s+)?\[(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]:[0-9]+\s+[A-Z0-9]+(.+)$"
+ )
+ meek_lite_pattern = re.compile(
+ "(meek_lite)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)(\s)+url=(.+)(\s)+front=(.+)"
+ )
+ if (
+ ipv4_pattern.match(bridge)
+ or ipv6_pattern.match(bridge)
+ or meek_lite_pattern.match(bridge)
+ ):
+ new_bridges.append(bridge)
+ bridges_valid = True
+
+ if bridges_valid:
+ new_bridges = "\n".join(new_bridges) + "\n"
+ settings.set("tor_bridges_use_custom_bridges", new_bridges)
+ else:
+ Alert(self.common, strings._("gui_settings_tor_bridges_invalid"))
+ return False
+ else:
settings.set("no_bridges", True)
- settings.set("tor_bridges_use_obfs4", False)
- settings.set("tor_bridges_use_meek_lite_azure", False)
- settings.set("tor_bridges_use_snowflake", False)
- settings.set("tor_bridges_use_moat", False)
- settings.set("tor_bridges_use_custom_bridges", "")
- if self.bridge_obfs4_radio.isChecked():
- settings.set("no_bridges", False)
- settings.set("tor_bridges_use_obfs4", True)
- settings.set("tor_bridges_use_meek_lite_azure", False)
- settings.set("tor_bridges_use_snowflake", False)
- settings.set("tor_bridges_use_moat", False)
- settings.set("tor_bridges_use_custom_bridges", "")
- if self.bridge_meek_azure_radio.isChecked():
- settings.set("no_bridges", False)
- settings.set("tor_bridges_use_obfs4", False)
- settings.set("tor_bridges_use_meek_lite_azure", True)
- settings.set("tor_bridges_use_snowflake", False)
- settings.set("tor_bridges_use_moat", False)
- settings.set("tor_bridges_use_custom_bridges", "")
- if self.bridge_snowflake_radio.isChecked():
- settings.set("no_bridges", False)
- settings.set("tor_bridges_use_obfs4", False)
- settings.set("tor_bridges_use_meek_lite_azure", False)
- settings.set("tor_bridges_use_snowflake", True)
- settings.set("tor_bridges_use_moat", False)
- settings.set("tor_bridges_use_custom_bridges", "")
- if self.bridge_moat_radio.isChecked():
- settings.set("no_bridges", False)
- settings.set("tor_bridges_use_obfs4", False)
- settings.set("tor_bridges_use_meek_lite_azure", False)
- settings.set("tor_bridges_use_snowflake", False)
- settings.set("tor_bridges_use_moat", True)
- settings.set(
- "tor_bridges_use_moat_bridges", self.bridge_moat_textbox.toPlainText()
- )
- settings.set("tor_bridges_use_custom_bridges", "")
- if self.bridge_custom_radio.isChecked():
- settings.set("no_bridges", False)
- settings.set("tor_bridges_use_obfs4", False)
- settings.set("tor_bridges_use_meek_lite_azure", False)
- settings.set("tor_bridges_use_snowflake", False)
- settings.set("tor_bridges_use_moat", False)
- settings.set("tor_bridges_use_moat_bridges", "")
-
- # Insert a 'Bridge' line at the start of each bridge.
- # This makes it easier to copy/paste a set of bridges
- # provided from https://bridges.torproject.org
- new_bridges = []
- bridges = self.bridge_custom_textbox.toPlainText().split("\n")
- bridges_valid = False
- for bridge in bridges:
- if bridge != "":
- # Check the syntax of the custom bridge to make sure it looks legitimate
- ipv4_pattern = re.compile(
- "(obfs4\s+)?(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):([0-9]+)(\s+)([A-Z0-9]+)(.+)$"
- )
- ipv6_pattern = re.compile(
- "(obfs4\s+)?\[(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]:[0-9]+\s+[A-Z0-9]+(.+)$"
- )
- meek_lite_pattern = re.compile(
- "(meek_lite)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)(\s)+url=(.+)(\s)+front=(.+)"
- )
- if (
- ipv4_pattern.match(bridge)
- or ipv6_pattern.match(bridge)
- or meek_lite_pattern.match(bridge)
- ):
- new_bridges.append("".join(["Bridge ", bridge, "\n"]))
- bridges_valid = True
-
- if bridges_valid:
- new_bridges = "".join(new_bridges)
- settings.set("tor_bridges_use_custom_bridges", new_bridges)
- else:
- Alert(self.common, strings._("gui_settings_tor_bridges_invalid"))
- settings.set("no_bridges", True)
- return False
return settings