Merge branch '435_support_bridges' of https://github.com/mig5/onionshare into mig5-435_support_bridges

This commit is contained in:
Micah Lee 2018-01-17 16:19:30 -08:00
commit 713e45084a
No known key found for this signature in database
GPG key ID: 403C2657CD994F73
15 changed files with 720 additions and 506 deletions

View file

@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
import sys, platform, datetime
import sys, platform, datetime, re
from onionshare import strings, common
from onionshare.settings import Settings
@ -140,6 +140,51 @@ class SettingsDialog(QtWidgets.QDialog):
if (system == 'Windows' or system == 'Darwin') and getattr(sys, 'onionshare_dev_mode', False):
self.connection_type_bundled_radio.setEnabled(False)
# Bridge options for bundled tor
# No bridges option radio
self.tor_bridges_no_bridges_radio = QtWidgets.QRadioButton(strings._('gui_settings_tor_bridges_no_bridges_radio_option', True))
self.tor_bridges_no_bridges_radio.toggled.connect(self.tor_bridges_no_bridges_radio_toggled)
# obfs4 option radio
# if the obfs4proxy binary is missing, we can't use obfs4 transports
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = common.get_tor_paths()
if not os.path.isfile(self.obfs4proxy_file_path):
self.tor_bridges_use_obfs4_radio = QtWidgets.QRadioButton(strings._('gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy', True))
self.tor_bridges_use_obfs4_radio.setEnabled(False)
else:
self.tor_bridges_use_obfs4_radio = QtWidgets.QRadioButton(strings._('gui_settings_tor_bridges_obfs4_radio_option', True))
self.tor_bridges_use_obfs4_radio.toggled.connect(self.tor_bridges_use_obfs4_radio_toggled)
# Custom bridges radio and textbox
self.tor_bridges_use_custom_radio = QtWidgets.QRadioButton(strings._('gui_settings_tor_bridges_custom_radio_option', True))
self.tor_bridges_use_custom_radio.toggled.connect(self.tor_bridges_use_custom_radio_toggled)
self.tor_bridges_use_custom_label = QtWidgets.QLabel(strings._('gui_settings_tor_bridges_custom_label', True))
self.tor_bridges_use_custom_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
self.tor_bridges_use_custom_label.setOpenExternalLinks(True)
self.tor_bridges_use_custom_textbox = QtWidgets.QPlainTextEdit()
self.tor_bridges_use_custom_textbox.setMaximumHeight(200)
self.tor_bridges_use_custom_textbox.setPlaceholderText('[address:port] [identifier]')
tor_bridges_use_custom_textbox_options_layout = QtWidgets.QVBoxLayout()
tor_bridges_use_custom_textbox_options_layout.addWidget(self.tor_bridges_use_custom_label)
tor_bridges_use_custom_textbox_options_layout.addWidget(self.tor_bridges_use_custom_textbox)
self.tor_bridges_use_custom_textbox_options = QtWidgets.QWidget()
self.tor_bridges_use_custom_textbox_options.setLayout(tor_bridges_use_custom_textbox_options_layout)
self.tor_bridges_use_custom_textbox_options.hide()
# Bridges layout/widget
bridges_layout = QtWidgets.QVBoxLayout()
bridges_layout.addWidget(self.tor_bridges_no_bridges_radio)
bridges_layout.addWidget(self.tor_bridges_use_obfs4_radio)
bridges_layout.addWidget(self.tor_bridges_use_custom_radio)
bridges_layout.addWidget(self.tor_bridges_use_custom_textbox_options)
self.bridges = QtWidgets.QWidget()
self.bridges.setLayout(bridges_layout)
# Automatic
self.connection_type_automatic_radio = QtWidgets.QRadioButton(strings._('gui_settings_connection_type_automatic_option', True))
self.connection_type_automatic_radio.toggled.connect(self.connection_type_automatic_toggled)
@ -238,6 +283,13 @@ class SettingsDialog(QtWidgets.QDialog):
connection_type_group = QtWidgets.QGroupBox()
connection_type_group.setLayout(connection_type_group_layout)
# The Bridges options are not exclusive (enabling Bridges offers obfs4 or custom bridges)
connection_type_bridges_radio_group_layout = QtWidgets.QVBoxLayout()
connection_type_bridges_radio_group_layout.addWidget(self.bridges)
self.connection_type_bridges_radio_group = QtWidgets.QGroupBox(strings._("gui_settings_tor_bridges", True))
self.connection_type_bridges_radio_group.setLayout(connection_type_bridges_radio_group_layout)
self.connection_type_bridges_radio_group.hide()
# Buttons
self.save_button = QtWidgets.QPushButton(strings._('gui_settings_button_save', True))
self.save_button.clicked.connect(self.save_clicked)
@ -266,6 +318,7 @@ class SettingsDialog(QtWidgets.QDialog):
right_col_layout = QtWidgets.QVBoxLayout()
right_col_layout.addWidget(connection_type_radio_group)
right_col_layout.addWidget(connection_type_group)
right_col_layout.addWidget(self.connection_type_bridges_radio_group)
right_col_layout.addWidget(self.tor_status)
right_col_layout.addStretch()
@ -345,6 +398,25 @@ class SettingsDialog(QtWidgets.QDialog):
self.authenticate_password_radio.setChecked(True)
self.authenticate_password_extras_password.setText(self.old_settings.get('auth_password'))
if self.old_settings.get('no_bridges'):
self.tor_bridges_no_bridges_radio.setChecked(True)
self.tor_bridges_use_obfs4_radio.setChecked(False)
self.tor_bridges_use_custom_radio.setChecked(False)
else:
self.tor_bridges_no_bridges_radio.setChecked(False)
self.tor_bridges_use_obfs4_radio.setChecked(self.old_settings.get('tor_bridges_use_obfs4'))
if self.old_settings.get('tor_bridges_use_custom_bridges'):
self.tor_bridges_use_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.tor_bridges_use_custom_textbox.setPlainText(new_bridges)
def connection_type_bundled_toggled(self, checked):
"""
Connection type bundled was toggled. If checked, hide authentication fields.
@ -353,6 +425,28 @@ class SettingsDialog(QtWidgets.QDialog):
if checked:
self.authenticate_group.hide()
self.connection_type_socks.hide()
self.connection_type_bridges_radio_group.show()
def tor_bridges_no_bridges_radio_toggled(self, checked):
"""
'No bridges' option was toggled. If checked, enable other bridge options.
"""
if checked:
self.tor_bridges_use_custom_textbox_options.hide()
def tor_bridges_use_obfs4_radio_toggled(self, checked):
"""
obfs4 bridges option was toggled. If checked, disable custom bridge options.
"""
if checked:
self.tor_bridges_use_custom_textbox_options.hide()
def tor_bridges_use_custom_radio_toggled(self, checked):
"""
Custom bridges option was toggled. If checked, show custom bridge options.
"""
if checked:
self.tor_bridges_use_custom_textbox_options.show()
def connection_type_automatic_toggled(self, checked):
"""
@ -362,6 +456,7 @@ class SettingsDialog(QtWidgets.QDialog):
if checked:
self.authenticate_group.hide()
self.connection_type_socks.hide()
self.connection_type_bridges_radio_group.hide()
def connection_type_control_port_toggled(self, checked):
"""
@ -373,6 +468,7 @@ class SettingsDialog(QtWidgets.QDialog):
self.authenticate_group.show()
self.connection_type_control_port_extras.show()
self.connection_type_socks.show()
self.connection_type_bridges_radio_group.hide()
else:
self.connection_type_control_port_extras.hide()
@ -387,6 +483,7 @@ class SettingsDialog(QtWidgets.QDialog):
self.authenticate_group.show()
self.connection_type_socket_file_extras.show()
self.connection_type_socks.show()
self.connection_type_bridges_radio_group.hide()
else:
self.connection_type_socket_file_extras.hide()
@ -513,7 +610,9 @@ class SettingsDialog(QtWidgets.QDialog):
if changed(settings, self.old_settings, [
'connection_type', 'control_port_address',
'control_port_port', 'socks_address', 'socks_port',
'socket_file_path', 'auth_type', 'auth_password']):
'socket_file_path', 'auth_type', 'auth_password',
'no_bridges', 'tor_bridges_use_obfs4',
'tor_bridges_use_custom_bridges']):
reboot_onion = True
@ -614,6 +713,38 @@ class SettingsDialog(QtWidgets.QDialog):
settings.set('auth_password', self.authenticate_password_extras_password.text())
# Whether we use bridges
if self.tor_bridges_no_bridges_radio.isChecked():
settings.set('no_bridges', True)
settings.set('tor_bridges_use_obfs4', False)
settings.set('tor_bridges_use_custom_bridges', '')
if self.tor_bridges_use_obfs4_radio.isChecked():
settings.set('no_bridges', False)
settings.set('tor_bridges_use_obfs4', True)
settings.set('tor_bridges_use_custom_bridges', '')
if self.tor_bridges_use_custom_radio.isChecked():
settings.set('no_bridges', False)
settings.set('tor_bridges_use_obfs4', False)
# 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.tor_bridges_use_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
pattern = re.compile("[0-9.]+:[0-9]+\s[A-Z0-9]+$")
if 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(strings._('gui_settings_tor_bridges_invalid', True))
settings.set('no_bridges', True)
return settings
def closeEvent(self, e):