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.