Completely refactor common to make a Common class, and pass that class down into all parts of the program

This commit is contained in:
Micah Lee 2018-03-08 10:18:31 -08:00
parent 49e352d131
commit 50409167d4
No known key found for this signature in database
GPG Key ID: 403C2657CD994F73
17 changed files with 458 additions and 444 deletions

View File

@ -20,7 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, sys, time, argparse, threading
from . import strings, common
from . import strings
from .common import Common
from .web import Web
from .onion import *
from .onionshare import OnionShare
@ -31,11 +32,13 @@ def main(cwd=None):
The main() function implements all of the logic that the command-line version of
onionshare uses.
"""
common = Common()
strings.load_strings(common)
print(strings._('version_string').format(common.get_version()))
print(strings._('version_string').format(common.version))
# OnionShare CLI in OSX needs to change current working directory (#132)
if common.get_platform() == 'Darwin':
if common.platform == 'Darwin':
if cwd:
os.chdir(cwd)
@ -69,8 +72,7 @@ def main(cwd=None):
sys.exit()
# Debug mode?
if debug:
common.set_debug(debug)
common.debug = debug
# Validate filenames
if not receive:
@ -86,7 +88,7 @@ def main(cwd=None):
sys.exit()
# Load settings
settings = Settings(config)
settings = Settings(common, config)
settings.load()
# In receive mode, validate downloads dir
@ -105,10 +107,10 @@ def main(cwd=None):
sys.exit()
# Create the Web object
web = Web(debug, stay_open, False, receive)
web = Web(common, stay_open, False, receive)
# Start the Onion object
onion = Onion()
onion = Onion(common)
try:
onion.connect(settings=False, config=config)
except (TorTooOld, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported, BundledTorTimeout) as e:
@ -119,7 +121,7 @@ def main(cwd=None):
# Start the onionshare app
try:
app = OnionShare(onion, local_only, stay_open, shutdown_timeout)
app = OnionShare(common, onion, local_only, stay_open, shutdown_timeout)
app.set_stealth(stealth)
app.start_onion_service()
except KeyboardInterrupt:

View File

@ -29,212 +29,197 @@ import tempfile
import threading
import time
debug = False
def log(module, func, msg=None):
class Common(object):
"""
If debug mode is on, log error messages to stdout
The Common object is shared amongst all parts of OnionShare.
"""
global debug
if debug:
timestamp = time.strftime("%b %d %Y %X")
def __init__(self, debug=False):
self.debug = debug
final_msg = "[{}] {}.{}".format(timestamp, module, func)
if msg:
final_msg = '{}: {}'.format(final_msg, msg)
print(final_msg)
# The platform OnionShare is running on
self.platform = platform.system()
if self.platform.endswith('BSD'):
self.platform = 'BSD'
# The current version of OnionShare
with open(self.get_resource_path('version.txt')) as f:
self.version = f.read().strip()
def set_debug(new_debug):
global debug
debug = new_debug
def log(self, module, func, msg=None):
"""
If debug mode is on, log error messages to stdout
"""
if self.debug:
timestamp = time.strftime("%b %d %Y %X")
final_msg = "[{}] {}.{}".format(timestamp, module, func)
if msg:
final_msg = '{}: {}'.format(final_msg, msg)
print(final_msg)
def get_platform():
"""
Returns the platform OnionShare is running on.
"""
plat = platform.system()
if plat.endswith('BSD'):
plat = 'BSD'
return plat
def get_resource_path(self, filename):
"""
Returns the absolute path of a resource, regardless of whether OnionShare is installed
systemwide, and whether regardless of platform
"""
# On Windows, and in Windows dev mode, switch slashes in incoming filename to backslackes
if self.platform == 'Windows':
filename = filename.replace('/', '\\')
if getattr(sys, 'onionshare_dev_mode', False):
# Look for resources directory relative to python file
prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))), 'share')
if not os.path.exists(prefix):
# While running tests during stdeb bdist_deb, look 3 directories up for the share folder
prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(prefix)))), 'share')
def get_resource_path(filename):
"""
Returns the absolute path of a resource, regardless of whether OnionShare is installed
systemwide, and whether regardless of platform
"""
p = get_platform()
elif self.platform == 'BSD' or self.platform == 'Linux':
# Assume OnionShare is installed systemwide in Linux, since we're not running in dev mode
prefix = os.path.join(sys.prefix, 'share/onionshare')
# On Windows, and in Windows dev mode, switch slashes in incoming filename to backslackes
if p == 'Windows':
filename = filename.replace('/', '\\')
elif getattr(sys, 'frozen', False):
# Check if app is "frozen"
# https://pythonhosted.org/PyInstaller/#run-time-information
if self.platform == 'Darwin':
prefix = os.path.join(sys._MEIPASS, 'share')
elif self.platform == 'Windows':
prefix = os.path.join(os.path.dirname(sys.executable), 'share')
if getattr(sys, 'onionshare_dev_mode', False):
# Look for resources directory relative to python file
prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))), 'share')
if not os.path.exists(prefix):
# While running tests during stdeb bdist_deb, look 3 directories up for the share folder
prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(prefix)))), 'share')
return os.path.join(prefix, filename)
elif p == 'BSD' or p == 'Linux':
# Assume OnionShare is installed systemwide in Linux, since we're not running in dev mode
prefix = os.path.join(sys.prefix, 'share/onionshare')
def get_tor_paths(self):
if self.platform == 'Linux':
tor_path = '/usr/bin/tor'
tor_geo_ip_file_path = '/usr/share/tor/geoip'
tor_geo_ipv6_file_path = '/usr/share/tor/geoip6'
obfs4proxy_file_path = '/usr/bin/obfs4proxy'
elif self.platform == 'Windows':
base_path = os.path.join(os.path.dirname(os.path.dirname(self.get_resource_path(''))), 'tor')
tor_path = os.path.join(os.path.join(base_path, 'Tor'), 'tor.exe')
obfs4proxy_file_path = os.path.join(os.path.join(base_path, 'Tor'), 'obfs4proxy.exe')
tor_geo_ip_file_path = os.path.join(os.path.join(os.path.join(base_path, 'Data'), 'Tor'), 'geoip')
tor_geo_ipv6_file_path = os.path.join(os.path.join(os.path.join(base_path, 'Data'), 'Tor'), 'geoip6')
elif self.platform == 'Darwin':
base_path = os.path.dirname(os.path.dirname(os.path.dirname(self.get_resource_path(''))))
tor_path = os.path.join(base_path, 'Resources', 'Tor', 'tor')
tor_geo_ip_file_path = os.path.join(base_path, 'Resources', 'Tor', 'geoip')
tor_geo_ipv6_file_path = os.path.join(base_path, 'Resources', 'Tor', 'geoip6')
obfs4proxy_file_path = os.path.join(base_path, 'Resources', 'Tor', 'obfs4proxy')
elif self.platform == 'BSD':
tor_path = '/usr/local/bin/tor'
tor_geo_ip_file_path = '/usr/local/share/tor/geoip'
tor_geo_ipv6_file_path = '/usr/local/share/tor/geoip6'
obfs4proxy_file_path = '/usr/local/bin/obfs4proxy'
elif getattr(sys, 'frozen', False):
# Check if app is "frozen"
# https://pythonhosted.org/PyInstaller/#run-time-information
if p == 'Darwin':
prefix = os.path.join(sys._MEIPASS, 'share')
elif p == 'Windows':
prefix = os.path.join(os.path.dirname(sys.executable), 'share')
return (tor_path, tor_geo_ip_file_path, tor_geo_ipv6_file_path, obfs4proxy_file_path)
return os.path.join(prefix, filename)
def build_slug(self):
"""
Returns a random string made from two words from the wordlist, such as "deter-trig".
"""
with open(self.get_resource_path('wordlist.txt')) as f:
wordlist = f.read().split()
r = random.SystemRandom()
return '-'.join(r.choice(wordlist) for _ in range(2))
def get_tor_paths():
p = get_platform()
if p == 'Linux':
tor_path = '/usr/bin/tor'
tor_geo_ip_file_path = '/usr/share/tor/geoip'
tor_geo_ipv6_file_path = '/usr/share/tor/geoip6'
obfs4proxy_file_path = '/usr/bin/obfs4proxy'
elif p == 'Windows':
base_path = os.path.join(os.path.dirname(os.path.dirname(get_resource_path(''))), 'tor')
tor_path = os.path.join(os.path.join(base_path, 'Tor'), 'tor.exe')
obfs4proxy_file_path = os.path.join(os.path.join(base_path, 'Tor'), 'obfs4proxy.exe')
tor_geo_ip_file_path = os.path.join(os.path.join(os.path.join(base_path, 'Data'), 'Tor'), 'geoip')
tor_geo_ipv6_file_path = os.path.join(os.path.join(os.path.join(base_path, 'Data'), 'Tor'), 'geoip6')
elif p == 'Darwin':
base_path = os.path.dirname(os.path.dirname(os.path.dirname(get_resource_path(''))))
tor_path = os.path.join(base_path, 'Resources', 'Tor', 'tor')
tor_geo_ip_file_path = os.path.join(base_path, 'Resources', 'Tor', 'geoip')
tor_geo_ipv6_file_path = os.path.join(base_path, 'Resources', 'Tor', 'geoip6')
obfs4proxy_file_path = os.path.join(base_path, 'Resources', 'Tor', 'obfs4proxy')
elif p == 'BSD':
tor_path = '/usr/local/bin/tor'
tor_geo_ip_file_path = '/usr/local/share/tor/geoip'
tor_geo_ipv6_file_path = '/usr/local/share/tor/geoip6'
obfs4proxy_file_path = '/usr/local/bin/obfs4proxy'
@staticmethod
def random_string(num_bytes, output_len=None):
"""
Returns a random string with a specified number of bytes.
"""
b = os.urandom(num_bytes)
h = hashlib.sha256(b).digest()[:16]
s = base64.b32encode(h).lower().replace(b'=', b'').decode('utf-8')
if not output_len:
return s
return s[:output_len]
return (tor_path, tor_geo_ip_file_path, tor_geo_ipv6_file_path, obfs4proxy_file_path)
def get_version():
"""
Returns the version of OnionShare that is running.
"""
with open(get_resource_path('version.txt')) as f:
version = f.read().strip()
return version
def random_string(num_bytes, output_len=None):
"""
Returns a random string with a specified number of bytes.
"""
b = os.urandom(num_bytes)
h = hashlib.sha256(b).digest()[:16]
s = base64.b32encode(h).lower().replace(b'=', b'').decode('utf-8')
if not output_len:
return s
return s[:output_len]
def build_slug():
"""
Returns a random string made from two words from the wordlist, such as "deter-trig".
"""
with open(get_resource_path('wordlist.txt')) as f:
wordlist = f.read().split()
r = random.SystemRandom()
return '-'.join(r.choice(wordlist) for _ in range(2))
def human_readable_filesize(b):
"""
Returns filesize in a human readable format.
"""
thresh = 1024.0
if b < thresh:
return '{:.1f} B'.format(b)
units = ('KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
u = 0
b /= thresh
while b >= thresh:
@staticmethod
def human_readable_filesize(b):
"""
Returns filesize in a human readable format.
"""
thresh = 1024.0
if b < thresh:
return '{:.1f} B'.format(b)
units = ('KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
u = 0
b /= thresh
u += 1
return '{:.1f} {}'.format(b, units[u])
while b >= thresh:
b /= thresh
u += 1
return '{:.1f} {}'.format(b, units[u])
@staticmethod
def format_seconds(seconds):
"""Return a human-readable string of the format 1d2h3m4s"""
days, seconds = divmod(seconds, 86400)
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
def format_seconds(seconds):
"""Return a human-readable string of the format 1d2h3m4s"""
days, seconds = divmod(seconds, 86400)
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
human_readable = []
if days:
human_readable.append("{:.0f}d".format(days))
if hours:
human_readable.append("{:.0f}h".format(hours))
if minutes:
human_readable.append("{:.0f}m".format(minutes))
if seconds or not human_readable:
human_readable.append("{:.0f}s".format(seconds))
return ''.join(human_readable)
human_readable = []
if days:
human_readable.append("{:.0f}d".format(days))
if hours:
human_readable.append("{:.0f}h".format(hours))
if minutes:
human_readable.append("{:.0f}m".format(minutes))
if seconds or not human_readable:
human_readable.append("{:.0f}s".format(seconds))
return ''.join(human_readable)
@staticmethod
def estimated_time_remaining(bytes_downloaded, total_bytes, started):
now = time.time()
time_elapsed = now - started # in seconds
download_rate = bytes_downloaded / time_elapsed
remaining_bytes = total_bytes - bytes_downloaded
eta = remaining_bytes / download_rate
return format_seconds(eta)
@staticmethod
def get_available_port(min_port, max_port):
"""
Find a random available port within the given range.
"""
with socket.socket() as tmpsock:
while True:
try:
tmpsock.bind(("127.0.0.1", random.randint(min_port, max_port)))
break
except OSError as e:
raise OSError(e)
_, port = tmpsock.getsockname()
return port
def estimated_time_remaining(bytes_downloaded, total_bytes, started):
now = time.time()
time_elapsed = now - started # in seconds
download_rate = bytes_downloaded / time_elapsed
remaining_bytes = total_bytes - bytes_downloaded
eta = remaining_bytes / download_rate
return format_seconds(eta)
def get_available_port(min_port, max_port):
"""
Find a random available port within the given range.
"""
with socket.socket() as tmpsock:
while True:
try:
tmpsock.bind(("127.0.0.1", random.randint(min_port, max_port)))
break
except OSError as e:
raise OSError(e)
_, port = tmpsock.getsockname()
return port
def dir_size(start_path):
"""
Calculates the total size, in bytes, of all of the files in a directory.
"""
total_size = 0
for dirpath, dirnames, filenames in os.walk(start_path):
for f in filenames:
fp = os.path.join(dirpath, f)
if not os.path.islink(fp):
total_size += os.path.getsize(fp)
return total_size
@staticmethod
def dir_size(start_path):
"""
Calculates the total size, in bytes, of all of the files in a directory.
"""
total_size = 0
for dirpath, dirnames, filenames in os.walk(start_path):
for f in filenames:
fp = os.path.join(dirpath, f)
if not os.path.islink(fp):
total_size += os.path.getsize(fp)
return total_size
class ShutdownTimer(threading.Thread):
"""
Background thread sleeps t hours and returns.
"""
def __init__(self, time):
def __init__(self, common, time):
threading.Thread.__init__(self)
self.common = common
self.setDaemon(True)
self.time = time
def run(self):
log('Shutdown Timer', 'Server will shut down after {} seconds'.format(self.time))
self.common.log('Shutdown Timer', 'Server will shut down after {} seconds'.format(self.time))
time.sleep(self.time)
return 1

View File

@ -125,22 +125,22 @@ class Onion(object):
call this function and pass in a status string while connecting to tor. This
is necessary for status updates to reach the GUI.
"""
def __init__(self):
common.log('Onion', '__init__')
def __init__(self, common):
self.common = common
self.common.log('Onion', '__init__')
self.stealth = False
self.service_id = None
self.system = common.get_platform()
# Is bundled tor supported?
if (self.system == 'Windows' or self.system == 'Darwin') and getattr(sys, 'onionshare_dev_mode', False):
if (self.common.platform == 'Windows' or self.common.platform == 'Darwin') and getattr(sys, 'onionshare_dev_mode', False):
self.bundle_tor_supported = False
else:
self.bundle_tor_supported = True
# Set the path of the tor binary, for bundled tor
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = common.get_tor_paths()
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = self.common.get_tor_paths()
# The tor process
self.tor_proc = None
@ -149,13 +149,13 @@ class Onion(object):
self.connected_to_tor = False
def connect(self, settings=False, config=False, tor_status_update_func=None):
common.log('Onion', 'connect')
self.common.log('Onion', 'connect')
# Either use settings that are passed in, or load them from disk
if settings:
self.settings = settings
else:
self.settings = Settings(config)
self.settings = Settings(self.common, config)
self.settings.load()
# The Tor controller
@ -168,29 +168,29 @@ class Onion(object):
# Create a torrc for this session
self.tor_data_directory = tempfile.TemporaryDirectory()
if self.system == 'Windows':
if self.common.platform == 'Windows':
# Windows needs to use network ports, doesn't support unix sockets
torrc_template = open(common.get_resource_path('torrc_template-windows')).read()
torrc_template = open(self.common.get_resource_path('torrc_template-windows')).read()
try:
self.tor_control_port = common.get_available_port(1000, 65535)
self.tor_control_port = self.common.get_available_port(1000, 65535)
except:
raise OSError(strings._('no_available_port'))
self.tor_control_socket = None
self.tor_cookie_auth_file = os.path.join(self.tor_data_directory.name, 'cookie')
try:
self.tor_socks_port = common.get_available_port(1000, 65535)
self.tor_socks_port = self.common.get_available_port(1000, 65535)
except:
raise OSError(strings._('no_available_port'))
self.tor_torrc = os.path.join(self.tor_data_directory.name, 'torrc')
else:
# Linux, Mac and BSD can use unix sockets
with open(common.get_resource_path('torrc_template')) as f:
with open(self.common.get_resource_path('torrc_template')) as f:
torrc_template = f.read()
self.tor_control_port = None
self.tor_control_socket = os.path.join(self.tor_data_directory.name, 'control_socket')
self.tor_cookie_auth_file = os.path.join(self.tor_data_directory.name, 'cookie')
try:
self.tor_socks_port = common.get_available_port(1000, 65535)
self.tor_socks_port = self.common.get_available_port(1000, 65535)
except:
raise OSError(strings._('no_available_port'))
self.tor_torrc = os.path.join(self.tor_data_directory.name, 'torrc')
@ -208,17 +208,17 @@ class Onion(object):
# Bridge support
if self.settings.get('tor_bridges_use_obfs4'):
f.write('ClientTransportPlugin obfs4 exec {}\n'.format(self.obfs4proxy_file_path))
with open(common.get_resource_path('torrc_template-obfs4')) as o:
with open(self.common.get_resource_path('torrc_template-obfs4')) as o:
for line in o:
f.write(line)
elif self.settings.get('tor_bridges_use_meek_lite_amazon'):
f.write('ClientTransportPlugin meek_lite exec {}\n'.format(self.obfs4proxy_file_path))
with open(common.get_resource_path('torrc_template-meek_lite_amazon')) as o:
with open(self.common.get_resource_path('torrc_template-meek_lite_amazon')) as o:
for line in o:
f.write(line)
elif self.settings.get('tor_bridges_use_meek_lite_azure'):
f.write('ClientTransportPlugin meek_lite exec {}\n'.format(self.obfs4proxy_file_path))
with open(common.get_resource_path('torrc_template-meek_lite_azure')) as o:
with open(self.common.get_resource_path('torrc_template-meek_lite_azure')) as o:
for line in o:
f.write(line)
@ -232,7 +232,7 @@ class Onion(object):
# Execute a tor subprocess
start_ts = time.time()
if self.system == 'Windows':
if self.common.platform == 'Windows':
# In Windows, hide console window when opening tor.exe subprocess
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
@ -245,7 +245,7 @@ class Onion(object):
# Connect to the controller
try:
if self.system == 'Windows':
if self.common.platform == 'Windows':
self.c = Controller.from_port(port=self.tor_control_port)
self.c.authenticate()
else:
@ -270,7 +270,7 @@ class Onion(object):
if callable(tor_status_update_func):
if not tor_status_update_func(progress, summary):
# If the dialog was canceled, stop connecting to Tor
common.log('Onion', 'connect', 'tor_status_update_func returned false, canceling connecting to Tor')
self.common.log('Onion', 'connect', 'tor_status_update_func returned false, canceling connecting to Tor')
print()
return False
@ -322,7 +322,7 @@ class Onion(object):
socket_file_path = ''
if not found_tor:
try:
if self.system == 'Darwin':
if self.common.platform == 'Darwin':
socket_file_path = os.path.expanduser('~/Library/Application Support/TorBrowser-Data/Tor/control.socket')
self.c = Controller.from_socket_file(path=socket_file_path)
@ -334,11 +334,11 @@ class Onion(object):
# guessing the socket file name next
if not found_tor:
try:
if self.system == 'Linux' or self.system == 'BSD':
if self.common.platform == 'Linux' or self.common.platform == 'BSD':
socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid())
elif self.system == 'Darwin':
elif self.common.platform == 'Darwin':
socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid())
elif self.system == 'Windows':
elif self.common.platform == 'Windows':
# Windows doesn't support unix sockets
raise TorErrorAutomatic(strings._('settings_error_automatic'))
@ -424,7 +424,7 @@ class Onion(object):
Start a onion service on port 80, pointing to the given port, and
return the onion hostname.
"""
common.log('Onion', 'start_onion_service')
self.common.log('Onion', 'start_onion_service')
self.auth_string = None
if not self.supports_ephemeral:
@ -447,11 +447,11 @@ class Onion(object):
if self.settings.get('private_key'):
key_type = "RSA1024"
key_content = self.settings.get('private_key')
common.log('Onion', 'Starting a hidden service with a saved private key')
self.common.log('Onion', 'Starting a hidden service with a saved private key')
else:
key_type = "NEW"
key_content = "RSA1024"
common.log('Onion', 'Starting a hidden service with a new private key')
self.common.log('Onion', 'Starting a hidden service with a new private key')
try:
if basic_auth != None:
@ -498,17 +498,17 @@ class Onion(object):
"""
Stop onion services that were created earlier. If there's a tor subprocess running, kill it.
"""
common.log('Onion', 'cleanup')
self.common.log('Onion', 'cleanup')
# Cleanup the ephemeral onion services, if we have any
try:
onions = self.c.list_ephemeral_hidden_services()
for onion in onions:
try:
common.log('Onion', 'cleanup', 'trying to remove onion {}'.format(onion))
self.common.log('Onion', 'cleanup', 'trying to remove onion {}'.format(onion))
self.c.remove_ephemeral_hidden_service(onion)
except:
common.log('Onion', 'cleanup', 'could not remove onion {}.. moving on anyway'.format(onion))
self.common.log('Onion', 'cleanup', 'could not remove onion {}.. moving on anyway'.format(onion))
pass
except:
pass
@ -545,7 +545,7 @@ class Onion(object):
"""
Returns a (address, port) tuple for the Tor SOCKS port
"""
common.log('Onion', 'get_tor_socks_port')
self.common.log('Onion', 'get_tor_socks_port')
if self.settings.get('connection_type') == 'bundled':
return ('127.0.0.1', self.tor_socks_port)

View File

@ -28,8 +28,10 @@ class OnionShare(object):
OnionShare is the main application class. Pass in options and run
start_onion_service and it will do the magic.
"""
def __init__(self, onion, local_only=False, stay_open=False, shutdown_timeout=0):
common.log('OnionShare', '__init__')
def __init__(self, common, onion, local_only=False, stay_open=False, shutdown_timeout=0):
self.common = common
self.common.log('OnionShare', '__init__')
# The Onion object
self.onion = onion
@ -53,7 +55,7 @@ class OnionShare(object):
self.shutdown_timer = None
def set_stealth(self, stealth):
common.log('OnionShare', 'set_stealth', 'stealth={}'.format(stealth))
self.common.log('OnionShare', 'set_stealth', 'stealth={}'.format(stealth))
self.stealth = stealth
self.onion.stealth = stealth
@ -62,11 +64,11 @@ class OnionShare(object):
"""
Start the onionshare onion service.
"""
common.log('OnionShare', 'start_onion_service')
self.common.log('OnionShare', 'start_onion_service')
# Choose a random port
try:
self.port = common.get_available_port(17600, 17650)
self.port = self.common.get_available_port(17600, 17650)
except:
raise OSError(strings._('no_available_port'))
@ -75,7 +77,7 @@ class OnionShare(object):
return
if self.shutdown_timeout > 0:
self.shutdown_timer = ShutdownTimer(self.shutdown_timeout)
self.shutdown_timer = ShutdownTimer(self.common, self.shutdown_timeout)
self.onion_host = self.onion.start_onion_service(self.port)
@ -86,7 +88,7 @@ class OnionShare(object):
"""
Shut everything down and clean up temporary files, etc.
"""
common.log('OnionShare', 'cleanup')
self.common.log('OnionShare', 'cleanup')
# cleanup files
for filename in self.cleanup_filenames:

View File

@ -22,7 +22,7 @@ import json
import os
import platform
from . import strings, common
from . import strings
class Settings(object):
@ -32,8 +32,10 @@ class Settings(object):
which is to attempt to connect automatically using default Tor Browser
settings.
"""
def __init__(self, config=False):
common.log('Settings', '__init__')
def __init__(self, common, config=False):
self.common = common
self.common.log('Settings', '__init__')
# Default config
self.filename = self.build_filename()
@ -43,11 +45,11 @@ class Settings(object):
if os.path.isfile(config):
self.filename = config
else:
common.log('Settings', '__init__', 'Supplied config does not exist or is unreadable. Falling back to default location')
self.common.log('Settings', '__init__', 'Supplied config does not exist or is unreadable. Falling back to default location')
# These are the default settings. They will get overwritten when loading from disk
self.default_settings = {
'version': common.get_version(),
'version': self.common.version,
'connection_type': 'bundled',
'control_port_address': '127.0.0.1',
'control_port_port': 9051,
@ -110,12 +112,12 @@ class Settings(object):
"""
Load the settings from file.
"""
common.log('Settings', 'load')
self.common.log('Settings', 'load')
# If the settings file exists, load it
if os.path.exists(self.filename):
try:
common.log('Settings', 'load', 'Trying to load {}'.format(self.filename))
self.common.log('Settings', 'load', 'Trying to load {}'.format(self.filename))
with open(self.filename, 'r') as f:
self._settings = json.load(f)
self.fill_in_defaults()
@ -126,7 +128,7 @@ class Settings(object):
"""
Save settings to file.
"""
common.log('Settings', 'save')
self.common.log('Settings', 'save')
try:
os.makedirs(os.path.dirname(self.filename))

View File

@ -41,14 +41,16 @@ class Web(object):
"""
The Web object is the OnionShare web server, powered by flask
"""
def __init__(self, debug, stay_open, gui_mode, receive_mode=False):
def __init__(self, common, stay_open, gui_mode, receive_mode=False):
self.common = common
# The flask app
self.app = Flask(__name__,
static_folder=common.get_resource_path('static'),
template_folder=common.get_resource_path('templates'))
# Debug mode?
if debug:
if self.common.debug:
self.debug_mode()
# Stay open after the first download?
@ -107,7 +109,7 @@ class Web(object):
self.client_cancel = False
# shutting down the server only works within the context of flask, so the easiest way to do it is over http
self.shutdown_slug = common.random_string(16)
self.shutdown_slug = self.common.random_string(16)
# Define the ewb app routes
self.common_routes()
@ -143,7 +145,7 @@ class Web(object):
file_info=self.file_info,
filename=os.path.basename(self.zip_filename),
filesize=self.zip_filesize,
filesize_human=common.human_readable_filesize(self.zip_filesize)))
filesize_human=self.common.human_readable_filesize(self.zip_filesize)))
return self.add_security_headers(r)
@self.app.route("/<slug_candidate>/download")
@ -206,10 +208,9 @@ class Web(object):
percent = (1.0 * downloaded_bytes / self.zip_filesize) * 100
# only output to stdout if running onionshare in CLI mode, or if using Linux (#203, #304)
plat = common.get_platform()
if not self.gui_mode or plat == 'Linux' or plat == 'BSD':
if not self.gui_mode or self.common.platform == 'Linux' or self.common.platform == 'BSD':
sys.stdout.write(
"\r{0:s}, {1:.2f}% ".format(common.human_readable_filesize(downloaded_bytes), percent))
"\r{0:s}, {1:.2f}% ".format(self.common.human_readable_filesize(downloaded_bytes), percent))
sys.stdout.flush()
self.add_request(self.REQUEST_PROGRESS, path, {'id': download_id, 'bytes': downloaded_bytes})
@ -224,7 +225,7 @@ class Web(object):
fp.close()
if common.get_platform() != 'Darwin':
if self.common.platform != 'Darwin':
sys.stdout.write("\n")
# Download is finished
@ -315,17 +316,17 @@ class Web(object):
}
if os.path.isfile(filename):
info['size'] = os.path.getsize(filename)
info['size_human'] = common.human_readable_filesize(info['size'])
info['size_human'] = self.common.human_readable_filesize(info['size'])
self.file_info['files'].append(info)
if os.path.isdir(filename):
info['size'] = common.dir_size(filename)
info['size_human'] = common.human_readable_filesize(info['size'])
info['size'] = self.common.dir_size(filename)
info['size_human'] = self.common.human_readable_filesize(info['size'])
self.file_info['dirs'].append(info)
self.file_info['files'] = sorted(self.file_info['files'], key=lambda k: k['basename'])
self.file_info['dirs'] = sorted(self.file_info['dirs'], key=lambda k: k['basename'])
# zip up the files and folders
z = ZipWriter(processed_size_callback=processed_size_callback)
z = ZipWriter(self.common, processed_size_callback=processed_size_callback)
for info in self.file_info['files']:
z.add_file(info['filename'])
for info in self.file_info['dirs']:
@ -353,7 +354,7 @@ class Web(object):
if persistent_slug:
self.slug = persistent_slug
else:
self.slug = common.build_slug()
self.slug = self.common.build_slug()
def debug_mode(self):
"""
@ -424,11 +425,13 @@ class ZipWriter(object):
with. If a zip_filename is not passed in, it will use the default onionshare
filename.
"""
def __init__(self, zip_filename=None, processed_size_callback=None):
def __init__(self, common, zip_filename=None, processed_size_callback=None):
self.common = common
if zip_filename:
self.zip_filename = zip_filename
else:
self.zip_filename = '{0:s}/onionshare_{1:s}.zip'.format(tempfile.mkdtemp(), common.random_string(4, 6))
self.zip_filename = '{0:s}/onionshare_{1:s}.zip'.format(tempfile.mkdtemp(), self.common.random_string(4, 6))
self.z = zipfile.ZipFile(self.zip_filename, 'w', allowZip64=True)
self.processed_size_callback = processed_size_callback

View File

@ -22,7 +22,8 @@ import os, sys, platform, argparse
from .alert import Alert
from PyQt5 import QtCore, QtWidgets
from onionshare import strings, common
from onionshare import strings
from onionshare.common import Common
from onionshare.web import Web
from onionshare.onion import Onion
from onionshare.onionshare import OnionShare
@ -35,9 +36,8 @@ class Application(QtWidgets.QApplication):
This is Qt's QApplication class. It has been overridden to support threads
and the quick keyboard shortcut.
"""
def __init__(self):
system = common.get_platform()
if system == 'Linux' or system == 'BSD':
def __init__(self, common):
if common.platform == 'Linux' or common.platform == 'BSD':
self.setAttribute(QtCore.Qt.AA_X11InitThreads, True)
QtWidgets.QApplication.__init__(self, sys.argv)
self.installEventFilter(self)
@ -54,12 +54,14 @@ def main():
"""
The main() function implements all of the logic that the GUI version of onionshare uses.
"""
common = Common()
strings.load_strings(common)
print(strings._('version_string').format(common.get_version()))
print(strings._('version_string').format(common.version))
# Start the Qt app
global qtapp
qtapp = Application()
qtapp = Application(common)
# Parse arguments
parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=48))
@ -84,34 +86,33 @@ def main():
debug = bool(args.debug)
# Debug mode?
if debug:
common.set_debug(debug)
common.debug = debug
# Validation
if filenames:
valid = True
for filename in filenames:
if not os.path.isfile(filename) and not os.path.isdir(filename):
Alert(strings._("not_a_file", True).format(filename))
Alert(self.common, strings._("not_a_file", True).format(filename))
valid = False
if not os.access(filename, os.R_OK):
Alert(strings._("not_a_readable_file", True).format(filename))
Alert(self.common, strings._("not_a_readable_file", True).format(filename))
valid = False
if not valid:
sys.exit()
# Create the Web object
web = Web(debug, stay_open, True)
web = Web(common, stay_open, True)
# Start the Onion
onion = Onion()
onion = Onion(common)
# Start the OnionShare app
app = OnionShare(onion, local_only, stay_open, shutdown_timeout)
app = OnionShare(common, onion, local_only, stay_open, shutdown_timeout)
# Launch the gui
web.stay_open = stay_open
gui = OnionShareGui(web, onion, qtapp, app, filenames, config)
gui = OnionShareGui(common, web, onion, qtapp, app, filenames, config)
# Clean up when app quits
def shutdown():

View File

@ -19,18 +19,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import common
class Alert(QtWidgets.QMessageBox):
"""
An alert box dialog.
"""
def __init__(self, message, icon=QtWidgets.QMessageBox.NoIcon, buttons=QtWidgets.QMessageBox.Ok, autostart=True):
def __init__(self, common, message, icon=QtWidgets.QMessageBox.NoIcon, buttons=QtWidgets.QMessageBox.Ok, autostart=True):
super(Alert, self).__init__(None)
common.log('Alert', '__init__')
self.common = common
self.common.log('Alert', '__init__')
self.setWindowTitle("OnionShare")
self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
self.setText(message)
self.setIcon(icon)
self.setStandardButtons(buttons)

View File

@ -21,11 +21,13 @@ import time
from PyQt5 import QtCore, QtWidgets
from onionshare import strings, common
from onionshare import strings
class Download(object):
def __init__(self, download_id, total_bytes):
def __init__(self, common, download_id, total_bytes):
self.common = common
self.download_id = download_id
self.started = time.time()
self.total_bytes = total_bytes
@ -64,7 +66,7 @@ class Download(object):
self.progress_bar.setValue(downloaded_bytes)
if downloaded_bytes == self.progress_bar.total_bytes:
pb_fmt = strings._('gui_download_progress_complete').format(
common.format_seconds(time.time() - self.started))
self.common.format_seconds(time.time() - self.started))
else:
elapsed = time.time() - self.started
if elapsed < 10:
@ -72,10 +74,10 @@ class Download(object):
# This prevents a "Windows copy dialog"-esque experience at
# the beginning of the download.
pb_fmt = strings._('gui_download_progress_starting').format(
common.human_readable_filesize(downloaded_bytes))
self.common.human_readable_filesize(downloaded_bytes))
else:
pb_fmt = strings._('gui_download_progress_eta').format(
common.human_readable_filesize(downloaded_bytes),
self.common.human_readable_filesize(downloaded_bytes),
self.estimated_time_remaining)
self.progress_bar.setFormat(pb_fmt)
@ -85,7 +87,7 @@ class Download(object):
@property
def estimated_time_remaining(self):
return common.estimated_time_remaining(self.downloaded_bytes,
return self.common.estimated_time_remaining(self.downloaded_bytes,
self.total_bytes,
self.started)
@ -95,8 +97,11 @@ class Downloads(QtWidgets.QWidget):
The downloads chunk of the GUI. This lists all of the active download
progress bars.
"""
def __init__(self):
def __init__(self, common):
super(Downloads, self).__init__()
self.common = common
self.downloads = {}
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
@ -108,7 +113,7 @@ class Downloads(QtWidgets.QWidget):
self.parent().show()
# add it to the list
download = Download(download_id, total_bytes)
download = Download(self.common, download_id, total_bytes)
self.downloads[download_id] = download
self.layout.insertWidget(-1, download.progress_bar)

View File

@ -21,21 +21,24 @@ import os
from PyQt5 import QtCore, QtWidgets, QtGui
from .alert import Alert
from onionshare import strings, common
from onionshare import strings
class DropHereLabel(QtWidgets.QLabel):
"""
When there are no files or folders in the FileList yet, display the
'drop files here' message and graphic.
"""
def __init__(self, parent, image=False):
def __init__(self, common, parent, image=False):
self.parent = parent
super(DropHereLabel, self).__init__(parent=parent)
self.common = common
self.setAcceptDrops(True)
self.setAlignment(QtCore.Qt.AlignCenter)
if image:
self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(common.get_resource_path('images/logo_transparent.png'))))
self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(self.common.get_resource_path('images/logo_transparent.png'))))
else:
self.setText(strings._('gui_drag_and_drop', True))
self.setStyleSheet('color: #999999;')
@ -53,9 +56,12 @@ class DropCountLabel(QtWidgets.QLabel):
While dragging files over the FileList, this counter displays the
number of files you're dragging.
"""
def __init__(self, parent):
def __init__(self, common, parent):
self.parent = parent
super(DropCountLabel, self).__init__(parent=parent)
self.common = common
self.setAcceptDrops(True)
self.setAlignment(QtCore.Qt.AlignCenter)
self.setText(strings._('gui_drag_and_drop', True))
@ -74,16 +80,19 @@ class FileList(QtWidgets.QListWidget):
files_dropped = QtCore.pyqtSignal()
files_updated = QtCore.pyqtSignal()
def __init__(self, parent=None):
def __init__(self, common, parent=None):
super(FileList, self).__init__(parent)
self.common = common
self.setAcceptDrops(True)
self.setIconSize(QtCore.QSize(32, 32))
self.setSortingEnabled(True)
self.setMinimumHeight(205)
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.drop_here_image = DropHereLabel(self, True)
self.drop_here_text = DropHereLabel(self, False)
self.drop_count = DropCountLabel(self)
self.drop_here_image = DropHereLabel(self.common, self, True)
self.drop_here_text = DropHereLabel(self.common, self, False)
self.drop_count = DropCountLabel(self.common, self)
self.resizeEvent(None)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
@ -206,7 +215,7 @@ class FileList(QtWidgets.QListWidget):
if filename not in filenames:
if not os.access(filename, os.R_OK):
Alert(strings._("not_a_readable_file", True).format(filename))
Alert(self.common, strings._("not_a_readable_file", True).format(filename))
return
fileinfo = QtCore.QFileInfo(filename)
@ -215,10 +224,10 @@ class FileList(QtWidgets.QListWidget):
if os.path.isfile(filename):
size_bytes = fileinfo.size()
size_readable = common.human_readable_filesize(size_bytes)
size_readable = self.common.human_readable_filesize(size_bytes)
else:
size_bytes = common.dir_size(filename)
size_readable = common.human_readable_filesize(size_bytes)
size_bytes = self.common.dir_size(filename)
size_readable = self.common.human_readable_filesize(size_bytes)
# Create a new item
item = QtWidgets.QListWidgetItem()
@ -245,7 +254,7 @@ class FileList(QtWidgets.QListWidget):
item.item_button = QtWidgets.QPushButton()
item.item_button.setDefault(False)
item.item_button.setFlat(True)
item.item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) )
item.item_button.setIcon( QtGui.QIcon(self.common.get_resource_path('images/file_delete.png')) )
item.item_button.clicked.connect(delete_item)
item.item_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
@ -277,12 +286,15 @@ class FileSelection(QtWidgets.QVBoxLayout):
The list of files and folders in the GUI, as well as buttons to add and
delete the files and folders.
"""
def __init__(self):
def __init__(self, common):
super(FileSelection, self).__init__()
self.common = common
self.server_on = False
# File list
self.file_list = FileList()
self.file_list = FileList(self.common)
self.file_list.itemSelectionChanged.connect(self.update)
self.file_list.files_dropped.connect(self.update)
self.file_list.files_updated.connect(self.update)

View File

@ -24,7 +24,7 @@ import queue
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings, common
from onionshare.common import ShutdownTimer
from onionshare.common import Common, ShutdownTimer
from onionshare.settings import Settings
from onionshare.onion import *
@ -47,12 +47,13 @@ class OnionShareGui(QtWidgets.QMainWindow):
starting_server_step3 = QtCore.pyqtSignal()
starting_server_error = QtCore.pyqtSignal(str)
def __init__(self, web, onion, qtapp, app, filenames, config=False):
def __init__(self, common, web, onion, qtapp, app, filenames, config=False):
super(OnionShareGui, self).__init__()
self._initSystemTray()
self.common = common
self.common.log('OnionShareGui', '__init__')
common.log('OnionShareGui', '__init__')
self._initSystemTray()
self.web = web
self.onion = onion
@ -60,22 +61,22 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.app = app
self.setWindowTitle('OnionShare')
self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
self.setMinimumWidth(430)
# Load settings
self.config = config
self.settings = Settings(self.config)
self.settings = Settings(self.common, self.config)
self.settings.load()
# File selection
self.file_selection = FileSelection()
self.file_selection = FileSelection(self.common)
if filenames:
for filename in filenames:
self.file_selection.file_list.add_file(filename)
# Server status
self.server_status = ServerStatus(self.qtapp, self.app, self.web, self.file_selection, self.settings)
self.server_status = ServerStatus(self.common, self.qtapp, self.app, self.web, self.file_selection, self.settings)
self.server_status.server_started.connect(self.file_selection.server_started)
self.server_status.server_started.connect(self.start_server)
self.server_status.server_started.connect(self.update_server_status_indicator)
@ -107,7 +108,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.filesize_warning.hide()
# Downloads
self.downloads = Downloads()
self.downloads = Downloads(self.common)
self.downloads_container = QtWidgets.QScrollArea()
self.downloads_container.setWidget(self.downloads)
self.downloads_container.setWidgetResizable(True)
@ -147,13 +148,13 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.settings_button.setDefault(False)
self.settings_button.setFlat(True)
self.settings_button.setFixedWidth(40)
self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) )
self.settings_button.setIcon( QtGui.QIcon(self.common.get_resource_path('images/settings.png')) )
self.settings_button.clicked.connect(self.open_settings)
# Server status indicator on the status bar
self.server_status_image_stopped = QtGui.QImage(common.get_resource_path('images/server_stopped.png'))
self.server_status_image_working = QtGui.QImage(common.get_resource_path('images/server_working.png'))
self.server_status_image_started = QtGui.QImage(common.get_resource_path('images/server_started.png'))
self.server_status_image_stopped = QtGui.QImage(self.common.get_resource_path('images/server_stopped.png'))
self.server_status_image_working = QtGui.QImage(self.common.get_resource_path('images/server_working.png'))
self.server_status_image_started = QtGui.QImage(self.common.get_resource_path('images/server_started.png'))
self.server_status_image_label = QtWidgets.QLabel()
self.server_status_image_label.setFixedWidth(20)
self.server_status_label = QtWidgets.QLabel()
@ -221,7 +222,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.timer.timeout.connect(self.check_for_requests)
# Start the "Connecting to Tor" dialog, which calls onion.connect()
tor_con = TorConnectionDialog(self.qtapp, self.settings, self.onion)
tor_con = TorConnectionDialog(self.common, self.qtapp, self.settings, self.onion)
tor_con.canceled.connect(self._tor_connection_canceled)
tor_con.open_settings.connect(self._tor_connection_open_settings)
tor_con.start()
@ -244,7 +245,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
for index in range(self.file_selection.file_list.count()):
item = self.file_selection.file_list.item(index)
total_size_bytes += item.size_bytes
total_size_readable = common.human_readable_filesize(total_size_bytes)
total_size_readable = self.common.human_readable_filesize(total_size_bytes)
if file_count > 1:
self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable))
@ -259,7 +260,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.adjustSize()
def update_server_status_indicator(self):
common.log('OnionShareGui', 'update_server_status_indicator')
self.common.log('OnionShareGui', 'update_server_status_indicator')
# Set the status image
if self.server_status.status == self.server_status.STATUS_STOPPED:
@ -273,8 +274,6 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.server_status_label.setText(strings._('gui_status_indicator_started', True))
def _initSystemTray(self):
system = common.get_platform()
menu = QtWidgets.QMenu()
self.settingsAction = menu.addAction(strings._('gui_settings_window_title', True))
self.settingsAction.triggered.connect(self.open_settings)
@ -285,10 +284,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.systemTray = QtWidgets.QSystemTrayIcon(self)
# The convention is Mac systray icons are always grayscale
if system == 'Darwin':
self.systemTray.setIcon(QtGui.QIcon(common.get_resource_path('images/logo_grayscale.png')))
if self.common.platform == 'Darwin':
self.systemTray.setIcon(QtGui.QIcon(self.common.get_resource_path('images/logo_grayscale.png')))
else:
self.systemTray.setIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
self.systemTray.setIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
self.systemTray.setContextMenu(menu)
self.systemTray.show()
@ -297,10 +296,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
If the user cancels before Tor finishes connecting, ask if they want to
quit, or open settings.
"""
common.log('OnionShareGui', '_tor_connection_canceled')
self.common.log('OnionShareGui', '_tor_connection_canceled')
def ask():
a = Alert(strings._('gui_tor_connection_ask', True), QtWidgets.QMessageBox.Question, buttons=QtWidgets.QMessageBox.NoButton, autostart=False)
a = Alert(self.common, strings._('gui_tor_connection_ask', True), QtWidgets.QMessageBox.Question, buttons=QtWidgets.QMessageBox.NoButton, autostart=False)
settings_button = QtWidgets.QPushButton(strings._('gui_tor_connection_ask_open_settings', True))
quit_button = QtWidgets.QPushButton(strings._('gui_tor_connection_ask_quit', True))
a.addButton(settings_button, QtWidgets.QMessageBox.AcceptRole)
@ -310,12 +309,12 @@ class OnionShareGui(QtWidgets.QMainWindow):
if a.clickedButton() == settings_button:
# Open settings
common.log('OnionShareGui', '_tor_connection_canceled', 'Settings button clicked')
self.common.log('OnionShareGui', '_tor_connection_canceled', 'Settings button clicked')
self.open_settings()
if a.clickedButton() == quit_button:
# Quit
common.log('OnionShareGui', '_tor_connection_canceled', 'Quit button clicked')
self.common.log('OnionShareGui', '_tor_connection_canceled', 'Quit button clicked')
# Wait 1ms for the event loop to finish, then quit
QtCore.QTimer.singleShot(1, self.qtapp.quit)
@ -327,7 +326,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
The TorConnectionDialog wants to open the Settings dialog
"""
common.log('OnionShareGui', '_tor_connection_open_settings')
self.common.log('OnionShareGui', '_tor_connection_open_settings')
# Wait 1ms for the event loop to finish closing the TorConnectionDialog
QtCore.QTimer.singleShot(1, self.open_settings)
@ -336,10 +335,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
Open the SettingsDialog.
"""
common.log('OnionShareGui', 'open_settings')
self.common.log('OnionShareGui', 'open_settings')
def reload_settings():
common.log('OnionShareGui', 'open_settings', 'settings have changed, reloading')
self.common.log('OnionShareGui', 'open_settings', 'settings have changed, reloading')
self.settings.load()
# We might've stopped the main requests timer if a Tor connection failed.
# If we've reloaded settings, we probably succeeded in obtaining a new
@ -356,7 +355,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
if not self.settings.get('shutdown_timeout'):
self.server_status.shutdown_timeout_container.hide()
d = SettingsDialog(self.onion, self.qtapp, self.config)
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config)
d.settings_saved.connect(reload_settings)
d.exec_()
@ -368,7 +367,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
Start the onionshare server. This uses multiple threads to start the Tor onion
server and the web app.
"""
common.log('OnionShareGui', 'start_server')
self.common.log('OnionShareGui', 'start_server')
self.set_server_active(True)
@ -405,8 +404,8 @@ class OnionShareGui(QtWidgets.QMainWindow):
# wait for modules in thread to load, preventing a thread-related cx_Freeze crash
time.sleep(0.2)
common.log('OnionshareGui', 'start_server', 'Starting an onion thread')
self.t = OnionThread(function=start_onion_service, kwargs={'self': self})
self.common.log('OnionshareGui', 'start_server', 'Starting an onion thread')
self.t = OnionThread(self.common, function=start_onion_service, kwargs={'self': self})
self.t.daemon = True
self.t.start()
@ -414,7 +413,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
Step 2 in starting the onionshare server. Zipping up files.
"""
common.log('OnionShareGui', 'start_server_step2')
self.common.log('OnionShareGui', 'start_server_step2')
# add progress bar to the status bar, indicating the crunching of files.
self._zip_progress_bar = ZipProgressBar(0)
@ -451,7 +450,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
Step 3 in starting the onionshare server. This displays the large filesize
warning, if applicable.
"""
common.log('OnionShareGui', 'start_server_step3')
self.common.log('OnionShareGui', 'start_server_step3')
# Remove zip progress bar
if self._zip_progress_bar is not None:
@ -469,7 +468,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.timeout = now.secsTo(self.server_status.timeout)
# Set the shutdown timeout value
if self.timeout > 0:
self.app.shutdown_timer = ShutdownTimer(self.timeout)
self.app.shutdown_timer = ShutdownTimer(self.common, self.timeout)
self.app.shutdown_timer.start()
# The timeout has actually already passed since the user clicked Start. Probably the Onion service took too long to start.
else:
@ -480,11 +479,11 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
If there's an error when trying to start the onion service
"""
common.log('OnionShareGui', 'start_server_error')
self.common.log('OnionShareGui', 'start_server_error')
self.set_server_active(False)
Alert(error, QtWidgets.QMessageBox.Warning)
Alert(self.common, error, QtWidgets.QMessageBox.Warning)
self.server_status.stop_server()
if self._zip_progress_bar is not None:
self.status_bar.removeWidget(self._zip_progress_bar)
@ -503,7 +502,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
Stop the onionshare server.
"""
common.log('OnionShareGui', 'stop_server')
self.common.log('OnionShareGui', 'stop_server')
if self.server_status.status != self.server_status.STATUS_STOPPED:
try:
@ -527,13 +526,12 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
Check for updates in a new thread, if enabled.
"""
system = common.get_platform()
if system == 'Windows' or system == 'Darwin':
if self.common.platform == 'Windows' or self.common.platform == 'Darwin':
if self.settings.get('use_autoupdate'):
def update_available(update_url, installed_version, latest_version):
Alert(strings._("update_available", True).format(update_url, installed_version, latest_version))
Alert(self.common, strings._("update_available", True).format(update_url, installed_version, latest_version))
self.update_thread = UpdateThread(self.onion, self.config)
self.update_thread = UpdateThread(self.common, self.onion, self.config)
self.update_thread.update_available.connect(update_available)
self.update_thread.start()
@ -544,7 +542,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
if os.path.isfile(filename):
total_size += os.path.getsize(filename)
if os.path.isdir(filename):
total_size += common.dir_size(filename)
total_size += Common.dir_size(filename)
return total_size
def check_for_requests(self):
@ -594,7 +592,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
elif event["type"] == self.web.REQUEST_RATE_LIMIT:
self.stop_server()
Alert(strings._('error_rate_limit'), QtWidgets.QMessageBox.Critical)
Alert(self.common, strings._('error_rate_limit'), QtWidgets.QMessageBox.Critical)
elif event["type"] == self.web.REQUEST_PROGRESS:
self.downloads.update_download(event["data"]["id"], event["data"]["bytes"])
@ -655,7 +653,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
When the URL gets copied to the clipboard, display this in the status bar.
"""
common.log('OnionShareGui', 'copy_url')
self.common.log('OnionShareGui', 'copy_url')
if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
self.systemTray.showMessage(strings._('gui_copied_url_title', True), strings._('gui_copied_url', True))
@ -663,7 +661,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
When the stealth onion service HidServAuth gets copied to the clipboard, display this in the status bar.
"""
common.log('OnionShareGui', 'copy_hidservauth')
self.common.log('OnionShareGui', 'copy_hidservauth')
if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'):
self.systemTray.showMessage(strings._('gui_copied_hidservauth_title', True), strings._('gui_copied_hidservauth', True))
@ -697,9 +695,9 @@ class OnionShareGui(QtWidgets.QMainWindow):
Update the 'Downloads completed' info widget.
"""
if count == 0:
self.info_completed_downloads_image = common.get_resource_path('images/download_completed_none.png')
self.info_completed_downloads_image = self.common.get_resource_path('images/download_completed_none.png')
else:
self.info_completed_downloads_image = common.get_resource_path('images/download_completed.png')
self.info_completed_downloads_image = self.common.get_resource_path('images/download_completed.png')
self.info_completed_downloads_count.setText('<img src="{0:s}" /> {1:d}'.format(self.info_completed_downloads_image, count))
self.info_completed_downloads_count.setToolTip(strings._('info_completed_downloads_tooltip', True).format(count))
@ -708,17 +706,17 @@ class OnionShareGui(QtWidgets.QMainWindow):
Update the 'Downloads in progress' info widget.
"""
if count == 0:
self.info_in_progress_downloads_image = common.get_resource_path('images/download_in_progress_none.png')
self.info_in_progress_downloads_image = self.common.get_resource_path('images/download_in_progress_none.png')
else:
self.info_in_progress_downloads_image = common.get_resource_path('images/download_in_progress.png')
self.info_in_progress_downloads_image = self.common.get_resource_path('images/download_in_progress.png')
self.info_in_progress_downloads_count.setText('<img src="{0:s}" /> {1:d}'.format(self.info_in_progress_downloads_image, count))
self.info_in_progress_downloads_count.setToolTip(strings._('info_in_progress_downloads_tooltip', True).format(count))
def closeEvent(self, e):
common.log('OnionShareGui', 'closeEvent')
self.common.log('OnionShareGui', 'closeEvent')
try:
if self.server_status.status != self.server_status.STATUS_STOPPED:
common.log('OnionShareGui', 'closeEvent, opening warning dialog')
self.common.log('OnionShareGui', 'closeEvent, opening warning dialog')
dialog = QtWidgets.QMessageBox()
dialog.setWindowTitle(strings._('gui_quit_title', True))
dialog.setText(strings._('gui_quit_warning', True))
@ -803,9 +801,12 @@ class OnionThread(QtCore.QThread):
decided to cancel (in which case do not proceed with obtaining
the Onion address and starting the web server).
"""
def __init__(self, function, kwargs=None):
def __init__(self, common, function, kwargs=None):
super(OnionThread, self).__init__()
common.log('OnionThread', '__init__')
self.common = common
self.common.log('OnionThread', '__init__')
self.function = function
if not kwargs:
self.kwargs = {}
@ -813,6 +814,6 @@ class OnionThread(QtCore.QThread):
self.kwargs = kwargs
def run(self):
common.log('OnionThread', 'run')
self.common.log('OnionThread', 'run')
self.function(**self.kwargs)

View File

@ -21,7 +21,7 @@ import platform
from .alert import Alert
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings, common, settings
from onionshare import strings
class ServerStatus(QtWidgets.QWidget):
"""
@ -38,8 +38,11 @@ class ServerStatus(QtWidgets.QWidget):
STATUS_WORKING = 1
STATUS_STARTED = 2
def __init__(self, qtapp, app, web, file_selection, settings):
def __init__(self, common, qtapp, app, web, file_selection, settings):
super(ServerStatus, self).__init__()
self.common = common
self.status = self.STATUS_STOPPED
self.qtapp = qtapp
@ -129,7 +132,7 @@ class ServerStatus(QtWidgets.QWidget):
if self.status == self.STATUS_STARTED:
self.url_description.show()
info_image = common.get_resource_path('images/info.png')
info_image = self.common.get_resource_path('images/info.png')
self.url_description.setText(strings._('gui_url_description', True).format(info_image))
# Show a Tool Tip explaining the lifecycle of this URL
if self.settings.get('save_private_key'):
@ -212,7 +215,7 @@ class ServerStatus(QtWidgets.QWidget):
self.timeout = self.shutdown_timeout.dateTime().toPyDateTime().replace(second=0, microsecond=0)
# If the timeout has actually passed already before the user hit Start, refuse to start the server.
if QtCore.QDateTime.currentDateTime().toPyDateTime() > self.timeout:
Alert(strings._('gui_server_timeout_expired', QtWidgets.QMessageBox.Warning))
Alert(self.common, strings._('gui_server_timeout_expired', QtWidgets.QMessageBox.Warning))
else:
self.start_server()
else:
@ -252,7 +255,7 @@ class ServerStatus(QtWidgets.QWidget):
"""
Cancel the server.
"""
common.log('ServerStatus', 'cancel_server', 'Canceling the server mid-startup')
self.common.log('ServerStatus', 'cancel_server', 'Canceling the server mid-startup')
self.status = self.STATUS_WORKING
self.shutdown_timeout_reset()
self.update()

View File

@ -34,9 +34,12 @@ class SettingsDialog(QtWidgets.QDialog):
"""
settings_saved = QtCore.pyqtSignal()
def __init__(self, onion, qtapp, config=False):
def __init__(self, common, onion, qtapp, config=False):
super(SettingsDialog, self).__init__()
common.log('SettingsDialog', '__init__')
self.common = common
self.common.log('SettingsDialog', '__init__')
self.onion = onion
self.qtapp = qtapp
@ -44,7 +47,7 @@ class SettingsDialog(QtWidgets.QDialog):
self.setModal(True)
self.setWindowTitle(strings._('gui_settings_window_title', True))
self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
self.system = platform.system()
@ -156,7 +159,7 @@ class SettingsDialog(QtWidgets.QDialog):
# 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()
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = self.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)
@ -166,7 +169,7 @@ class SettingsDialog(QtWidgets.QDialog):
# meek_lite-amazon option radio
# if the obfs4proxy binary is missing, we can't use meek_lite-amazon transports
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = common.get_tor_paths()
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = self.common.get_tor_paths()
if not os.path.isfile(self.obfs4proxy_file_path):
self.tor_bridges_use_meek_lite_amazon_radio = QtWidgets.QRadioButton(strings._('gui_settings_tor_bridges_meek_lite_amazon_radio_option_no_obfs4proxy', True))
self.tor_bridges_use_meek_lite_amazon_radio.setEnabled(False)
@ -176,7 +179,7 @@ class SettingsDialog(QtWidgets.QDialog):
# meek_lite-azure option radio
# if the obfs4proxy binary is missing, we can't use meek_lite-azure transports
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = common.get_tor_paths()
(self.tor_path, self.tor_geo_ip_file_path, self.tor_geo_ipv6_file_path, self.obfs4proxy_file_path) = self.common.get_tor_paths()
if not os.path.isfile(self.obfs4proxy_file_path):
self.tor_bridges_use_meek_lite_azure_radio = QtWidgets.QRadioButton(strings._('gui_settings_tor_bridges_meek_lite_azure_radio_option_no_obfs4proxy', True))
self.tor_bridges_use_meek_lite_azure_radio.setEnabled(False)
@ -330,7 +333,7 @@ class SettingsDialog(QtWidgets.QDialog):
self.save_button.clicked.connect(self.save_clicked)
self.cancel_button = QtWidgets.QPushButton(strings._('gui_settings_button_cancel', True))
self.cancel_button.clicked.connect(self.cancel_clicked)
version_label = QtWidgets.QLabel('OnionShare {0:s}'.format(common.get_version()))
version_label = QtWidgets.QLabel('OnionShare {0:s}'.format(self.common.version))
version_label.setStyleSheet('color: #666666')
self.help_button = QtWidgets.QPushButton(strings._('gui_settings_button_help', True))
self.help_button.clicked.connect(self.help_clicked)
@ -372,7 +375,7 @@ class SettingsDialog(QtWidgets.QDialog):
self.cancel_button.setFocus()
# Load settings, and fill them in
self.old_settings = Settings(self.config)
self.old_settings = Settings(self.common, self.config)
self.old_settings.load()
close_after_first_download = self.old_settings.get('close_after_first_download')
@ -470,7 +473,7 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Connection type bundled was toggled. If checked, hide authentication fields.
"""
common.log('SettingsDialog', 'connection_type_bundled_toggled')
self.common.log('SettingsDialog', 'connection_type_bundled_toggled')
if checked:
self.authenticate_group.hide()
self.connection_type_socks.hide()
@ -515,7 +518,7 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Connection type automatic was toggled. If checked, hide authentication fields.
"""
common.log('SettingsDialog', 'connection_type_automatic_toggled')
self.common.log('SettingsDialog', 'connection_type_automatic_toggled')
if checked:
self.authenticate_group.hide()
self.connection_type_socks.hide()
@ -526,7 +529,7 @@ class SettingsDialog(QtWidgets.QDialog):
Connection type control port was toggled. If checked, show extra fields
for Tor control address and port. If unchecked, hide those extra fields.
"""
common.log('SettingsDialog', 'connection_type_control_port_toggled')
self.common.log('SettingsDialog', 'connection_type_control_port_toggled')
if checked:
self.authenticate_group.show()
self.connection_type_control_port_extras.show()
@ -541,7 +544,7 @@ class SettingsDialog(QtWidgets.QDialog):
Connection type socket file was toggled. If checked, show extra fields
for socket file. If unchecked, hide those extra fields.
"""
common.log('SettingsDialog', 'connection_type_socket_file_toggled')
self.common.log('SettingsDialog', 'connection_type_socket_file_toggled')
if checked:
self.authenticate_group.show()
self.connection_type_socket_file_extras.show()
@ -554,14 +557,14 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Authentication option no authentication was toggled.
"""
common.log('SettingsDialog', 'authenticate_no_auth_toggled')
self.common.log('SettingsDialog', 'authenticate_no_auth_toggled')
def authenticate_password_toggled(self, checked):
"""
Authentication option password was toggled. If checked, show extra fields
for password auth. If unchecked, hide those extra fields.
"""
common.log('SettingsDialog', 'authenticate_password_toggled')
self.common.log('SettingsDialog', 'authenticate_password_toggled')
if checked:
self.authenticate_password_extras.show()
else:
@ -572,7 +575,7 @@ class SettingsDialog(QtWidgets.QDialog):
Toggle the 'Copy HidServAuth' button
to copy the saved HidServAuth to clipboard.
"""
common.log('SettingsDialog', 'hidservauth_copy_button_clicked', 'HidServAuth was copied to clipboard')
self.common.log('SettingsDialog', 'hidservauth_copy_button_clicked', 'HidServAuth was copied to clipboard')
clipboard = self.qtapp.clipboard()
clipboard.setText(self.old_settings.get('hidservauth_string'))
@ -581,7 +584,7 @@ class SettingsDialog(QtWidgets.QDialog):
Test Tor Settings button clicked. With the given settings, see if we can
successfully connect and authenticate to Tor.
"""
common.log('SettingsDialog', 'test_tor_clicked')
self.common.log('SettingsDialog', 'test_tor_clicked')
settings = self.settings_from_fields()
try:
@ -600,13 +603,13 @@ class SettingsDialog(QtWidgets.QDialog):
onion.connect(settings=settings, config=self.config, tor_status_update_func=tor_status_update_func)
# If an exception hasn't been raised yet, the Tor settings work
Alert(strings._('settings_test_success', True).format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth))
Alert(self.common, strings._('settings_test_success', True).format(onion.tor_version, onion.supports_ephemeral, onion.supports_stealth))
# Clean up
onion.cleanup()
except (TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorNotSupported, BundledTorTimeout) as e:
Alert(e.args[0], QtWidgets.QMessageBox.Warning)
Alert(self.common, e.args[0], QtWidgets.QMessageBox.Warning)
if settings.get('connection_type') == 'bundled':
self.tor_status.hide()
self._enable_buttons()
@ -615,14 +618,14 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Check for Updates button clicked. Manually force an update check.
"""
common.log('SettingsDialog', 'check_for_updates')
self.common.log('SettingsDialog', 'check_for_updates')
# Disable buttons
self._disable_buttons()
self.qtapp.processEvents()
def update_timestamp():
# Update the last checked label
settings = Settings(self.config)
settings = Settings(self.common, self.config)
settings.load()
autoupdate_timestamp = settings.get('autoupdate_timestamp')
self._update_autoupdate_timestamp(autoupdate_timestamp)
@ -636,22 +639,22 @@ class SettingsDialog(QtWidgets.QDialog):
# Check for updates
def update_available(update_url, installed_version, latest_version):
Alert(strings._("update_available", True).format(update_url, installed_version, latest_version))
Alert(self.common, strings._("update_available", True).format(update_url, installed_version, latest_version))
close_forced_update_thread()
def update_not_available():
Alert(strings._('update_not_available', True))
Alert(self.common, strings._('update_not_available', True))
close_forced_update_thread()
def update_error():
Alert(strings._('update_error_check_error', True), QtWidgets.QMessageBox.Warning)
Alert(self.common, strings._('update_error_check_error', True), QtWidgets.QMessageBox.Warning)
close_forced_update_thread()
def update_invalid_version():
Alert(strings._('update_error_invalid_latest_version', True).format(e.latest_version), QtWidgets.QMessageBox.Warning)
Alert(self.common, strings._('update_error_invalid_latest_version', True).format(e.latest_version), QtWidgets.QMessageBox.Warning)
close_forced_update_thread()
forced_update_thread = UpdateThread(self.onion, self.config, force=True)
forced_update_thread = UpdateThread(self.common, self.onion, self.config, force=True)
forced_update_thread.update_available.connect(update_available)
forced_update_thread.update_not_available.connect(update_not_available)
forced_update_thread.update_error.connect(update_error)
@ -662,7 +665,7 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Save button clicked. Save current settings to disk.
"""
common.log('SettingsDialog', 'save_clicked')
self.common.log('SettingsDialog', 'save_clicked')
settings = self.settings_from_fields()
if settings:
@ -672,7 +675,7 @@ class SettingsDialog(QtWidgets.QDialog):
# the Onion object
reboot_onion = False
if self.onion.is_authenticated():
common.log('SettingsDialog', 'save_clicked', 'Connected to Tor')
self.common.log('SettingsDialog', 'save_clicked', 'Connected to Tor')
def changed(s1, s2, keys):
"""
Compare the Settings objects s1 and s2 and return true if any values
@ -694,20 +697,20 @@ class SettingsDialog(QtWidgets.QDialog):
reboot_onion = True
else:
common.log('SettingsDialog', 'save_clicked', 'Not connected to Tor')
self.common.log('SettingsDialog', 'save_clicked', 'Not connected to Tor')
# Tor isn't connected, so try connecting
reboot_onion = True
# Do we need to reinitialize Tor?
if reboot_onion:
# Reinitialize the Onion object
common.log('SettingsDialog', 'save_clicked', 'rebooting the Onion')
self.common.log('SettingsDialog', 'save_clicked', 'rebooting the Onion')
self.onion.cleanup()
tor_con = TorConnectionDialog(self.qtapp, settings, self.onion)
tor_con = TorConnectionDialog(self.common, self.qtapp, settings, self.onion)
tor_con.start()
common.log('SettingsDialog', 'save_clicked', 'Onion done rebooting, connected to Tor: {}'.format(self.onion.connected_to_tor))
self.common.log('SettingsDialog', 'save_clicked', 'Onion done rebooting, connected to Tor: {}'.format(self.onion.connected_to_tor))
if self.onion.is_authenticated() and not tor_con.wasCanceled():
self.settings_saved.emit()
@ -721,9 +724,9 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Cancel button clicked.
"""
common.log('SettingsDialog', 'cancel_clicked')
self.common.log('SettingsDialog', 'cancel_clicked')
if not self.onion.is_authenticated():
Alert(strings._('gui_tor_connection_canceled', True), QtWidgets.QMessageBox.Warning)
Alert(self.common, strings._('gui_tor_connection_canceled', True), QtWidgets.QMessageBox.Warning)
sys.exit()
else:
self.close()
@ -732,7 +735,7 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Help button clicked.
"""
common.log('SettingsDialog', 'help_clicked')
self.common.log('SettingsDialog', 'help_clicked')
help_site = 'https://github.com/micahflee/onionshare/wiki'
QtGui.QDesktopServices.openUrl(QtCore.QUrl(help_site))
@ -740,8 +743,8 @@ class SettingsDialog(QtWidgets.QDialog):
"""
Return a Settings object that's full of values from the settings dialog.
"""
common.log('SettingsDialog', 'settings_from_fields')
settings = Settings(self.config)
self.common.log('SettingsDialog', 'settings_from_fields')
settings = Settings(self.common, self.config)
settings.load() # To get the last update timestamp
settings.set('close_after_first_download', self.close_after_first_download_checkbox.isChecked())
@ -846,24 +849,24 @@ class SettingsDialog(QtWidgets.QDialog):
new_bridges = ''.join(new_bridges)
settings.set('tor_bridges_use_custom_bridges', new_bridges)
else:
Alert(strings._('gui_settings_tor_bridges_invalid', True))
Alert(self.common, strings._('gui_settings_tor_bridges_invalid', True))
settings.set('no_bridges', True)
return False
return settings
def closeEvent(self, e):
common.log('SettingsDialog', 'closeEvent')
self.common.log('SettingsDialog', 'closeEvent')
# On close, if Tor isn't connected, then quit OnionShare altogether
if not self.onion.is_authenticated():
common.log('SettingsDialog', 'closeEvent', 'Closing while not connected to Tor')
self.common.log('SettingsDialog', 'closeEvent', 'Closing while not connected to Tor')
# Wait 1ms for the event loop to finish, then quit
QtCore.QTimer.singleShot(1, self.qtapp.quit)
def _update_autoupdate_timestamp(self, autoupdate_timestamp):
common.log('SettingsDialog', '_update_autoupdate_timestamp')
self.common.log('SettingsDialog', '_update_autoupdate_timestamp')
if autoupdate_timestamp:
dt = datetime.datetime.fromtimestamp(autoupdate_timestamp)
@ -880,7 +883,7 @@ class SettingsDialog(QtWidgets.QDialog):
self._enable_buttons()
def _disable_buttons(self):
common.log('SettingsDialog', '_disable_buttons')
self.common.log('SettingsDialog', '_disable_buttons')
self.check_for_updates_button.setEnabled(False)
self.connection_type_test_button.setEnabled(False)
@ -888,7 +891,7 @@ class SettingsDialog(QtWidgets.QDialog):
self.cancel_button.setEnabled(False)
def _enable_buttons(self):
common.log('SettingsDialog', '_enable_buttons')
self.common.log('SettingsDialog', '_enable_buttons')
# We can't check for updates if we're still not connected to Tor
if not self.onion.connected_to_tor:
self.check_for_updates_button.setEnabled(False)

View File

@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings, common
from onionshare import strings
from onionshare.onion import *
from .alert import Alert
@ -30,16 +30,19 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
"""
open_settings = QtCore.pyqtSignal()
def __init__(self, qtapp, settings, onion):
def __init__(self, common, qtapp, settings, onion):
super(TorConnectionDialog, self).__init__(None)
common.log('TorConnectionDialog', '__init__')
self.common = common
self.common.log('TorConnectionDialog', '__init__')
self.qtapp = qtapp
self.settings = settings
self.onion = onion
self.setWindowTitle("OnionShare")
self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png')))
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path('images/logo.png')))
self.setModal(True)
self.setFixedSize(400, 150)
@ -55,9 +58,9 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
self._tor_status_update(0, '')
def start(self):
common.log('TorConnectionDialog', 'start')
self.common.log('TorConnectionDialog', 'start')
t = TorConnectionThread(self, self.settings, self.onion)
t = TorConnectionThread(self.common, self, self.settings, self.onion)
t.tor_status_update.connect(self._tor_status_update)
t.connected_to_tor.connect(self._connected_to_tor)
t.canceled_connecting_to_tor.connect(self._canceled_connecting_to_tor)
@ -77,14 +80,14 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
self.setLabelText("<strong>{}</strong><br>{}".format(strings._('connecting_to_tor', True), summary))
def _connected_to_tor(self):
common.log('TorConnectionDialog', '_connected_to_tor')
self.common.log('TorConnectionDialog', '_connected_to_tor')
self.active = False
# Close the dialog after connecting
self.setValue(self.maximum())
def _canceled_connecting_to_tor(self):
common.log('TorConnectionDialog', '_canceled_connecting_to_tor')
self.common.log('TorConnectionDialog', '_canceled_connecting_to_tor')
self.active = False
self.onion.cleanup()
@ -92,12 +95,12 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
QtCore.QTimer.singleShot(1, self.cancel)
def _error_connecting_to_tor(self, msg):
common.log('TorConnectionDialog', '_error_connecting_to_tor')
self.common.log('TorConnectionDialog', '_error_connecting_to_tor')
self.active = False
def alert_and_open_settings():
# Display the exception in an alert box
Alert("{}\n\n{}".format(msg, strings._('gui_tor_connection_error_settings', True)), QtWidgets.QMessageBox.Warning)
Alert(self.common, "{}\n\n{}".format(msg, strings._('gui_tor_connection_error_settings', True)), QtWidgets.QMessageBox.Warning)
# Open settings
self.open_settings.emit()
@ -113,16 +116,19 @@ class TorConnectionThread(QtCore.QThread):
canceled_connecting_to_tor = QtCore.pyqtSignal()
error_connecting_to_tor = QtCore.pyqtSignal(str)
def __init__(self, dialog, settings, onion):
def __init__(self, common, dialog, settings, onion):
super(TorConnectionThread, self).__init__()
common.log('TorConnectionThread', '__init__')
self.common = common
self.common.log('TorConnectionThread', '__init__')
self.dialog = dialog
self.settings = settings
self.onion = onion
def run(self):
common.log('TorConnectionThread', 'run')
self.common.log('TorConnectionThread', 'run')
# Connect to the Onion
try:
@ -133,11 +139,11 @@ class TorConnectionThread(QtCore.QThread):
self.canceled_connecting_to_tor.emit()
except BundledTorCanceled as e:
common.log('TorConnectionThread', 'run', 'caught exception: BundledTorCanceled')
self.common.log('TorConnectionThread', 'run', 'caught exception: BundledTorCanceled')
self.canceled_connecting_to_tor.emit()
except Exception as e:
common.log('TorConnectionThread', 'run', 'caught exception: {}'.format(e.args[0]))
self.common.log('TorConnectionThread', 'run', 'caught exception: {}'.format(e.args[0]))
self.error_connecting_to_tor.emit(str(e.args[0]))
def _tor_status_update(self, progress, summary):

View File

@ -25,7 +25,7 @@ from onionshare import socks
from onionshare.settings import Settings
from onionshare.onion import Onion
from . import strings, common
from . import strings
class UpdateCheckerCheckError(Exception):
"""
@ -55,16 +55,19 @@ class UpdateChecker(QtCore.QObject):
update_error = QtCore.pyqtSignal()
update_invalid_version = QtCore.pyqtSignal()
def __init__(self, onion, config=False):
def __init__(self, common, onion, config=False):
super(UpdateChecker, self).__init__()
common.log('UpdateChecker', '__init__')
self.common = common
self.common.log('UpdateChecker', '__init__')
self.onion = onion
self.config = config
def check(self, force=False, config=False):
common.log('UpdateChecker', 'check', 'force={}'.format(force))
self.common.log('UpdateChecker', 'check', 'force={}'.format(force))
# Load the settings
settings = Settings(config)
settings = Settings(self.common, config)
settings.load()
# If force=True, then definitely check
@ -87,11 +90,11 @@ class UpdateChecker(QtCore.QObject):
# Check for updates
if check_for_updates:
common.log('UpdateChecker', 'check', 'checking for updates')
self.common.log('UpdateChecker', 'check', 'checking for updates')
# Download the latest-version file over Tor
try:
# User agent string includes OnionShare version and platform
user_agent = 'OnionShare {}, {}'.format(common.get_version(), platform.system())
user_agent = 'OnionShare {}, {}'.format(self.common.version, self.common.platform)
# If the update is forced, add '?force=1' to the URL, to more
# accurately measure daily users
@ -104,7 +107,7 @@ class UpdateChecker(QtCore.QObject):
else:
onion_domain = 'elx57ue5uyfplgva.onion'
common.log('UpdateChecker', 'check', 'loading http://{}{}'.format(onion_domain, path))
self.common.log('UpdateChecker', 'check', 'loading http://{}{}'.format(onion_domain, path))
(socks_address, socks_port) = self.onion.get_tor_socks_port()
socks.set_default_proxy(socks.SOCKS5, socks_address, socks_port)
@ -122,10 +125,10 @@ class UpdateChecker(QtCore.QObject):
http_response = s.recv(1024)
latest_version = http_response[http_response.find(b'\r\n\r\n'):].strip().decode('utf-8')
common.log('UpdateChecker', 'check', 'latest OnionShare version: {}'.format(latest_version))
self.common.log('UpdateChecker', 'check', 'latest OnionShare version: {}'.format(latest_version))
except Exception as e:
common.log('UpdateChecker', 'check', '{}'.format(e))
self.common.log('UpdateChecker', 'check', '{}'.format(e))
self.update_error.emit()
raise UpdateCheckerCheckError
@ -145,7 +148,7 @@ class UpdateChecker(QtCore.QObject):
# Do we need to update?
update_url = 'https://github.com/micahflee/onionshare/releases/tag/v{}'.format(latest_version)
installed_version = common.get_version()
installed_version = self.common.version
if installed_version < latest_version:
self.update_available.emit(update_url, installed_version, latest_version)
return
@ -159,17 +162,20 @@ class UpdateThread(QtCore.QThread):
update_error = QtCore.pyqtSignal()
update_invalid_version = QtCore.pyqtSignal()
def __init__(self, onion, config=False, force=False):
def __init__(self, common, onion, config=False, force=False):
super(UpdateThread, self).__init__()
common.log('UpdateThread', '__init__')
self.common = common
self.common.log('UpdateThread', '__init__')
self.onion = onion
self.config = config
self.force = force
def run(self):
common.log('UpdateThread', 'run')
self.common.log('UpdateThread', 'run')
u = UpdateChecker(self.onion, self.config)
u = UpdateChecker(self.common, self.onion, self.config)
u.update_available.connect(self._update_available)
u.update_not_available.connect(self._update_not_available)
u.update_error.connect(self._update_error)
@ -179,25 +185,25 @@ class UpdateThread(QtCore.QThread):
u.check(config=self.config,force=self.force)
except Exception as e:
# If update check fails, silently ignore
common.log('UpdateThread', 'run', '{}'.format(e))
self.common.log('UpdateThread', 'run', '{}'.format(e))
pass
def _update_available(self, update_url, installed_version, latest_version):
common.log('UpdateThread', '_update_available')
self.common.log('UpdateThread', '_update_available')
self.active = False
self.update_available.emit(update_url, installed_version, latest_version)
def _update_not_available(self):
common.log('UpdateThread', '_update_not_available')
self.common.log('UpdateThread', '_update_not_available')
self.active = False
self.update_not_available.emit()
def _update_error(self):
common.log('UpdateThread', '_update_error')
self.common.log('UpdateThread', '_update_error')
self.active = False
self.update_error.emit()
def _update_invalid_version(self):
common.log('UpdateThread', '_update_invalid_version')
self.common.log('UpdateThread', '_update_invalid_version')
self.active = False
self.update_invalid_version.emit()

View File

@ -157,13 +157,13 @@ class TestGetAvailablePort:
class TestGetPlatform:
def test_darwin(self, platform_darwin):
assert common.get_platform() == 'Darwin'
assert common.platform == 'Darwin'
def test_linux(self, platform_linux):
assert common.get_platform() == 'Linux'
assert common.platform == 'Linux'
def test_windows(self, platform_windows):
assert common.get_platform() == 'Windows'
assert common.platform == 'Windows'
# TODO: double-check these tests
@ -235,14 +235,6 @@ class TestGetTorPaths:
(tor_path, tor_geo_ip_file_path, tor_geo_ipv6_file_path, obfs4proxy_file_path))
class TestGetVersion:
def test_get_version(self, sys_onionshare_dev_mode):
with open(common.get_resource_path('version.txt')) as f:
version = f.read().strip()
assert version == common.get_version()
class TestHumanReadableFilesize:
@pytest.mark.parametrize('test_input,expected', (
(1024 ** 0, '1.0 B'),
@ -284,13 +276,3 @@ class TestLog:
line_one, line_two, _ = output.split('\n')
assert LOG_MSG_REGEX.match(line_one)
assert LOG_MSG_REGEX.match(line_two)
class TestSetDebug:
def test_debug_true(self, set_debug_false):
common.set_debug(True)
assert common.debug is True
def test_debug_false(self, set_debug_true):
common.set_debug(False)
assert common.debug is False

View File

@ -28,7 +28,7 @@ from onionshare import common, settings, strings
@pytest.fixture
def custom_version(monkeypatch):
monkeypatch.setattr(common, 'get_version', lambda: 'DUMMY_VERSION_1.2.3')
monkeypatch.setattr(common, 'version', 'DUMMY_VERSION_1.2.3')
@pytest.fixture