Refactor UpdateChecker into a class that's a QObject, and make it use signals and slots to communicate

This commit is contained in:
Micah Lee 2017-04-15 18:55:41 -07:00
parent b645dccad1
commit 772ccee8b5
2 changed files with 74 additions and 57 deletions

View File

@ -381,9 +381,17 @@ class SettingsDialog(QtWidgets.QDialog):
bundled_tor_func = None bundled_tor_func = None
# Check for updates # 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: try:
if not check_for_updates(force=True, bundled_tor_func=bundled_tor_func): u.check(force=True, bundled_tor_func=bundled_tor_func)
Alert(strings._('update_not_available', True))
except UpdateCheckerTorError: except UpdateCheckerTorError:
Alert(strings._('update_error_tor', True), QtWidgets.QMessageBox.Warning) Alert(strings._('update_error_tor', True), QtWidgets.QMessageBox.Warning)
except UpdateCheckerSOCKSHTTPError: except UpdateCheckerSOCKSHTTPError:

View File

@ -17,12 +17,12 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from PyQt5 import QtCore
import datetime, time, socks, socket, re, platform import datetime, time, socks, socket, re, platform
from . import strings, helpers from . import strings, helpers
from onionshare.settings import Settings from onionshare.settings import Settings
from onionshare.onion import Onion from onionshare.onion import Onion
from .alert import Alert
class UpdateCheckerTorError(Exception): class UpdateCheckerTorError(Exception):
""" """
@ -44,7 +44,7 @@ class UpdateCheckerInvalidLatestVersion(Exception):
def __init__(self, latest_version): def __init__(self, latest_version):
self.latest_version = 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 Load http://elx57ue5uyfplgva.onion/latest-version.txt to see what the latest
version of OnionShare is. If the latest version is newer than the 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. Only check at most once per day, unless force is True.
""" """
# Load the settings update_available = QtCore.pyqtSignal(str, str, str)
settings = Settings() update_not_available = QtCore.pyqtSignal()
settings.load()
# See if it's been 1 day since the last check, and if so set force to True def __init__(self):
if not force: super(UpdateChecker, self).__init__()
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) def check(self, force=False, bundled_tor_func=None):
if now - last_checked > one_day: # 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 force = True
else:
force = True
# Check for updates # Check for updates
if force: if force:
# Create an Onion object, for checking for updates over tor # Create an Onion object, for checking for updates over tor
try: try:
onion = Onion(settings=settings, bundled_tor_func=bundled_tor_func) onion = Onion(settings=settings, bundled_tor_func=bundled_tor_func)
except: except:
raise UpdateCheckerTorError raise UpdateCheckerTorError
# Download the latest-version file over Tor # Download the latest-version file over Tor
try: try:
(socks_address, socks_port) = onion.get_tor_socks_port() (socks_address, socks_port) = onion.get_tor_socks_port()
socks.set_default_proxy(socks.SOCKS5, socks_address, socks_port) socks.set_default_proxy(socks.SOCKS5, socks_address, socks_port)
s = socks.socksocket() s = socks.socksocket()
s.connect(('elx57ue5uyfplgva.onion', 80)) s.settimeout(15) # 15 second timeout
s.connect(('elx57ue5uyfplgva.onion', 80))
http_request = 'GET /latest-version.txt HTTP/1.0\r\n' http_request = 'GET /latest-version.txt HTTP/1.0\r\n'
http_request += 'Host: elx57ue5uyfplgva.onion\r\n' http_request += 'Host: elx57ue5uyfplgva.onion\r\n'
http_request += 'User-Agent: OnionShare {}, {}\r\n'.format(helpers.get_version(), platform.system()) http_request += 'User-Agent: OnionShare {}, {}\r\n'.format(helpers.get_version(), platform.system())
http_request += '\r\n' http_request += '\r\n'
s.sendall(http_request.encode('utf-8')) s.sendall(http_request.encode('utf-8'))
http_response = s.recv(1024) http_response = s.recv(1024)
latest_version = http_response[http_response.find(b'\r\n\r\n'):].strip().decode('utf-8') latest_version = http_response[http_response.find(b'\r\n\r\n'):].strip().decode('utf-8')
# Clean up from Onion # Clean up from Onion
onion.cleanup() onion.cleanup()
except: except:
raise UpdateCheckerSOCKSHTTPError raise UpdateCheckerSOCKSHTTPError
# Validate that latest_version looks like a version string # Validate that latest_version looks like a version string
# This regex is: 1-3 dot-separated numeric components # This regex is: 1-3 dot-separated numeric components
version_re = r"^(\d+\.)?(\d+\.)?(\d+)$" version_re = r"^(\d+\.)?(\d+\.)?(\d+)$"
if not re.match(version_re, latest_version): if not re.match(version_re, latest_version):
raise UpdateCheckerInvalidLatestVersion(latest_version) raise UpdateCheckerInvalidLatestVersion(latest_version)
# Update the last checked timestamp (dropping the seconds and milliseconds) # Update the last checked timestamp (dropping the seconds and milliseconds)
timestamp = datetime.datetime.now().replace(microsecond=0).replace(second=0).timestamp() timestamp = datetime.datetime.now().replace(microsecond=0).replace(second=0).timestamp()
settings.set('autoupdate_timestamp', timestamp) settings.set('autoupdate_timestamp', timestamp)
settings.save() settings.save()
# Do we need to update? # Do we need to update?
update_url = 'https://github.com/micahflee/onionshare/releases/tag/v{}'.format(latest_version) update_url = 'https://github.com/micahflee/onionshare/releases/tag/v{}'.format(latest_version)
installed_version = helpers.get_version() installed_version = helpers.get_version()
if installed_version < latest_version: if installed_version < latest_version:
Alert(strings._("update_available", True).format(update_url, installed_version, latest_version)) self.update_available.emit(update_url, installed_version, latest_version)
return True return
return False # No updates are available
self.update_not_available.emit()