diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json
index 51a3d53a..97ce5585 100644
--- a/desktop/src/onionshare/resources/locale/en.json
+++ b/desktop/src/onionshare/resources/locale/en.json
@@ -68,6 +68,8 @@
"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_moat_error": "Error requesting a bridge from torproject.org.",
"gui_settings_bridge_custom_radio_option": "Provide a bridge you learned about from a trusted source",
"gui_settings_tor_bridges_invalid": "None of the bridges you added work.\nDouble-check them or add others.",
"gui_settings_button_save": "Save",
diff --git a/desktop/src/onionshare/tor_settings_dialog.py b/desktop/src/onionshare/tor_settings_dialog.py
index abecf949..9f08d767 100644
--- a/desktop/src/onionshare/tor_settings_dialog.py
+++ b/desktop/src/onionshare/tor_settings_dialog.py
@@ -19,30 +19,14 @@ along with this program. If not, see .
"""
from PySide2 import QtCore, QtWidgets, QtGui
-from PySide2.QtCore import Slot, Qt
-from PySide2.QtGui import QPalette, QColor
import sys
import platform
-import datetime
import re
import os
+import requests
+
from onionshare_cli.settings import Settings
-from onionshare_cli.onion import (
- Onion,
- TorErrorInvalidSetting,
- TorErrorAutomatic,
- TorErrorSocketPort,
- TorErrorSocketFile,
- TorErrorMissingPassword,
- TorErrorUnreadableCookieFile,
- TorErrorAuthError,
- TorErrorProtocolError,
- BundledTorTimeout,
- BundledTorBroken,
- TorTooOldEphemeral,
- TorTooOldStealth,
- PortNotAvailable,
-)
+from onionshare_cli.onion import Onion
from . import strings
from .widgets import Alert
@@ -159,10 +143,17 @@ class TorSettingsDialog(QtWidgets.QDialog):
strings._("gui_settings_bridge_moat_radio_option")
)
self.bridge_moat_radio.toggled.connect(self.bridge_moat_radio_toggled)
+ self.bridge_moat_button = QtWidgets.QPushButton(
+ strings._("gui_settings_bridge_moat_button")
+ )
+ self.bridge_moat_button.setMinimumHeight(20)
+ self.bridge_moat_button.clicked.connect(self.bridge_moat_button_clicked)
self.bridge_moat_textbox = QtWidgets.QPlainTextEdit()
self.bridge_moat_textbox.setMaximumHeight(200)
self.bridge_moat_textbox.setEnabled(False)
+ self.bridge_moat_textbox.hide()
bridge_moat_textbox_options_layout = QtWidgets.QVBoxLayout()
+ 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.setLayout(bridge_moat_textbox_options_layout)
@@ -511,6 +502,52 @@ class TorSettingsDialog(QtWidgets.QDialog):
self.bridge_custom_textbox_options.hide()
self.bridge_moat_textbox_options.show()
+ def bridge_moat_button_clicked(self):
+ """
+ Request new bridge button clicked
+ """
+ self.common.log("TorSettingsDialog", "bridge_moat_button_clicked")
+
+ def moat_error():
+ Alert(
+ self.common,
+ strings._("gui_settings_bridge_moat_error"),
+ title=strings._("gui_settings_bridge_moat_button"),
+ )
+
+ # TODO: Do all of this using domain fronting
+
+ # Request a bridge
+ r = requests.post(
+ "https://bridges.torproject.org/moat/fetch",
+ headers={"Content-Type": "application/vnd.api+json"},
+ json={
+ "data": [
+ {
+ "version": "0.1.0",
+ "type": "client-transports",
+ "supported": ["obfs4"],
+ }
+ ]
+ },
+ )
+ if r.status_code != 200:
+ return moat_error()
+
+ try:
+ moat_res = r.json()
+ if "errors" in moat_res or "data" not in moat_res:
+ return moat_error()
+ if moat_res["type"] != "moat-challenge":
+ return moat_error()
+
+ moat_type = moat_res["type"]
+ moat_transport = moat_res["transport"]
+ moat_image = moat_res["image"]
+ moat_challenge = moat_res["challenge"]
+ except:
+ return moat_error()
+
def bridge_custom_radio_toggled(self, checked):
"""
Custom bridges option was toggled. If checked, show custom bridge options.