From 47c1488512bdb0538fc29ac94ecbe1908cc13f00 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sat, 15 Apr 2017 18:55:41 -0700 Subject: [PATCH] Refactor UpdateChecker into a class that's a QObject, and make it use signals and slots to communicate --- onionshare_gui/settings_dialog.py | 12 ++- onionshare_gui/update_checker.py | 119 ++++++++++++++++-------------- 2 files changed, 74 insertions(+), 57 deletions(-) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index 1194383a..1b1e2bff 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -381,9 +381,17 @@ class SettingsDialog(QtWidgets.QDialog): bundled_tor_func = None # Check for updates + def update_available(update_url, installed_version, latest_version): + Alert(strings._("update_available", True).format(update_url, installed_version, latest_version)) + def update_not_available(): + Alert(strings._('update_not_available', True)) + + u = UpdateChecker() + u.update_available.connect(update_available) + u.update_not_available.connect(update_not_available) + try: - if not check_for_updates(force=True, bundled_tor_func=bundled_tor_func): - Alert(strings._('update_not_available', True)) + u.check(force=True, bundled_tor_func=bundled_tor_func) except UpdateCheckerTorError: Alert(strings._('update_error_tor', True), QtWidgets.QMessageBox.Warning) except UpdateCheckerSOCKSHTTPError: diff --git a/onionshare_gui/update_checker.py b/onionshare_gui/update_checker.py index aaaa59b2..f3d3fcd4 100644 --- a/onionshare_gui/update_checker.py +++ b/onionshare_gui/update_checker.py @@ -17,12 +17,12 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ +from PyQt5 import QtCore import datetime, time, socks, socket, re, platform from . import strings, helpers from onionshare.settings import Settings from onionshare.onion import Onion -from .alert import Alert class UpdateCheckerTorError(Exception): """ @@ -44,7 +44,7 @@ class UpdateCheckerInvalidLatestVersion(Exception): def __init__(self, latest_version): self.latest_version = latest_version -def check_for_updates(force=False, bundled_tor_func=None): +class UpdateChecker(QtCore.QObject): """ Load http://elx57ue5uyfplgva.onion/latest-version.txt to see what the latest version of OnionShare is. If the latest version is newer than the @@ -52,69 +52,78 @@ def check_for_updates(force=False, bundled_tor_func=None): Only check at most once per day, unless force is True. """ - # Load the settings - settings = Settings() - settings.load() + update_available = QtCore.pyqtSignal(str, str, str) + update_not_available = QtCore.pyqtSignal() - # See if it's been 1 day since the last check, and if so set force to True - if not force: - autoupdate_timestamp = settings.get('autoupdate_timestamp') - if autoupdate_timestamp: - last_checked = datetime.datetime.fromtimestamp(autoupdate_timestamp) - now = datetime.datetime.now() + def __init__(self): + super(UpdateChecker, self).__init__() - one_day = datetime.timedelta(days=1) - if now - last_checked > one_day: + def check(self, force=False, bundled_tor_func=None): + # Load the settings + settings = Settings() + settings.load() + + # See if it's been 1 day since the last check, and if so set force to True + if not force: + autoupdate_timestamp = settings.get('autoupdate_timestamp') + if autoupdate_timestamp: + last_checked = datetime.datetime.fromtimestamp(autoupdate_timestamp) + now = datetime.datetime.now() + + one_day = datetime.timedelta(days=1) + if now - last_checked > one_day: + force = True + else: force = True - else: - force = True - # Check for updates - if force: - # Create an Onion object, for checking for updates over tor - try: - onion = Onion(settings=settings, bundled_tor_func=bundled_tor_func) - except: - raise UpdateCheckerTorError + # Check for updates + if force: + # Create an Onion object, for checking for updates over tor + try: + onion = Onion(settings=settings, bundled_tor_func=bundled_tor_func) + except: + raise UpdateCheckerTorError - # Download the latest-version file over Tor - try: - (socks_address, socks_port) = onion.get_tor_socks_port() - socks.set_default_proxy(socks.SOCKS5, socks_address, socks_port) + # Download the latest-version file over Tor + try: + (socks_address, socks_port) = onion.get_tor_socks_port() + socks.set_default_proxy(socks.SOCKS5, socks_address, socks_port) - s = socks.socksocket() - s.connect(('elx57ue5uyfplgva.onion', 80)) + s = socks.socksocket() + s.settimeout(15) # 15 second timeout + s.connect(('elx57ue5uyfplgva.onion', 80)) - http_request = 'GET /latest-version.txt HTTP/1.0\r\n' - http_request += 'Host: elx57ue5uyfplgva.onion\r\n' - http_request += 'User-Agent: OnionShare {}, {}\r\n'.format(helpers.get_version(), platform.system()) - http_request += '\r\n' - s.sendall(http_request.encode('utf-8')) + http_request = 'GET /latest-version.txt HTTP/1.0\r\n' + http_request += 'Host: elx57ue5uyfplgva.onion\r\n' + http_request += 'User-Agent: OnionShare {}, {}\r\n'.format(helpers.get_version(), platform.system()) + http_request += '\r\n' + s.sendall(http_request.encode('utf-8')) - http_response = s.recv(1024) - latest_version = http_response[http_response.find(b'\r\n\r\n'):].strip().decode('utf-8') + http_response = s.recv(1024) + latest_version = http_response[http_response.find(b'\r\n\r\n'):].strip().decode('utf-8') - # Clean up from Onion - onion.cleanup() - except: - raise UpdateCheckerSOCKSHTTPError + # Clean up from Onion + onion.cleanup() + except: + raise UpdateCheckerSOCKSHTTPError - # Validate that latest_version looks like a version string - # This regex is: 1-3 dot-separated numeric components - version_re = r"^(\d+\.)?(\d+\.)?(\d+)$" - if not re.match(version_re, latest_version): - raise UpdateCheckerInvalidLatestVersion(latest_version) + # Validate that latest_version looks like a version string + # This regex is: 1-3 dot-separated numeric components + version_re = r"^(\d+\.)?(\d+\.)?(\d+)$" + if not re.match(version_re, latest_version): + raise UpdateCheckerInvalidLatestVersion(latest_version) - # Update the last checked timestamp (dropping the seconds and milliseconds) - timestamp = datetime.datetime.now().replace(microsecond=0).replace(second=0).timestamp() - settings.set('autoupdate_timestamp', timestamp) - settings.save() + # Update the last checked timestamp (dropping the seconds and milliseconds) + timestamp = datetime.datetime.now().replace(microsecond=0).replace(second=0).timestamp() + settings.set('autoupdate_timestamp', timestamp) + settings.save() - # Do we need to update? - update_url = 'https://github.com/micahflee/onionshare/releases/tag/v{}'.format(latest_version) - installed_version = helpers.get_version() - if installed_version < latest_version: - Alert(strings._("update_available", True).format(update_url, installed_version, latest_version)) - return True + # Do we need to update? + update_url = 'https://github.com/micahflee/onionshare/releases/tag/v{}'.format(latest_version) + installed_version = helpers.get_version() + if installed_version < latest_version: + self.update_available.emit(update_url, installed_version, latest_version) + return - return False + # No updates are available + self.update_not_available.emit()