mirror of
https://github.com/onionshare/onionshare.git
synced 2025-10-13 13:41:11 -04:00
Add onionshare CLI to cli folder, move GUI to desktop folder, and start refactoring it to work with briefcase
This commit is contained in:
parent
93e90c89ae
commit
a54f99adf6
583 changed files with 14871 additions and 474 deletions
234
desktop/onionshare_gui/update_checker.py
Normal file
234
desktop/onionshare_gui/update_checker.py
Normal file
|
@ -0,0 +1,234 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
OnionShare | https://onionshare.org/
|
||||
|
||||
Copyright (C) 2014-2020 Micah Lee, et al. <micah@micahflee.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from PyQt5 import QtCore
|
||||
import datetime, time, socket, re, platform
|
||||
import socks
|
||||
from distutils.version import LooseVersion as Version
|
||||
|
||||
from onionshare.settings import Settings
|
||||
from onionshare.onion import Onion
|
||||
|
||||
from onionshare import strings
|
||||
|
||||
|
||||
class UpdateCheckerCheckError(Exception):
|
||||
"""
|
||||
Error checking for updates because of some Tor connection issue, or because
|
||||
the OnionShare website is down.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UpdateCheckerInvalidLatestVersion(Exception):
|
||||
"""
|
||||
Successfully downloaded the latest version, but it doesn't appear to be a
|
||||
valid version string.
|
||||
"""
|
||||
|
||||
def __init__(self, latest_version):
|
||||
self.latest_version = latest_version
|
||||
|
||||
|
||||
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
|
||||
installed version, alert the user.
|
||||
|
||||
Only check at most once per day, unless force is True.
|
||||
"""
|
||||
|
||||
update_available = QtCore.pyqtSignal(str, str, str)
|
||||
update_not_available = QtCore.pyqtSignal()
|
||||
update_error = QtCore.pyqtSignal()
|
||||
update_invalid_version = QtCore.pyqtSignal(str)
|
||||
|
||||
def __init__(self, common, onion):
|
||||
super(UpdateChecker, self).__init__()
|
||||
|
||||
self.common = common
|
||||
|
||||
self.common.log("UpdateChecker", "__init__")
|
||||
self.onion = onion
|
||||
|
||||
def check(self, force=False):
|
||||
self.common.log("UpdateChecker", "check", f"force={force}")
|
||||
# Load the settings
|
||||
settings = Settings(self.common)
|
||||
settings.load()
|
||||
|
||||
# If force=True, then definitely check
|
||||
if force:
|
||||
check_for_updates = True
|
||||
else:
|
||||
check_for_updates = False
|
||||
|
||||
# See if it's been 1 day since the last check
|
||||
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:
|
||||
check_for_updates = True
|
||||
else:
|
||||
check_for_updates = True
|
||||
|
||||
# Check for updates
|
||||
if check_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 = f"OnionShare {self.common.version}, {self.common.platform}"
|
||||
|
||||
# If the update is forced, add '?force=1' to the URL, to more
|
||||
# accurately measure daily users
|
||||
path = "/latest-version.txt"
|
||||
if force:
|
||||
path += "?force=1"
|
||||
|
||||
if Version(self.onion.tor_version) >= Version("0.3.2.9"):
|
||||
onion_domain = (
|
||||
"lldan5gahapx5k7iafb3s4ikijc4ni7gx5iywdflkba5y2ezyg6sjgyd.onion"
|
||||
)
|
||||
else:
|
||||
onion_domain = "elx57ue5uyfplgva.onion"
|
||||
|
||||
self.common.log(
|
||||
"UpdateChecker", "check", f"loading http://{onion_domain}{path}"
|
||||
)
|
||||
|
||||
(socks_address, socks_port) = self.onion.get_tor_socks_port()
|
||||
socks.set_default_proxy(socks.SOCKS5, socks_address, socks_port)
|
||||
|
||||
s = socks.socksocket()
|
||||
s.settimeout(15) # 15 second timeout
|
||||
s.connect((onion_domain, 80))
|
||||
|
||||
http_request = f"GET {path} HTTP/1.0\r\n"
|
||||
http_request += f"Host: {onion_domain}\r\n"
|
||||
http_request += f"User-Agent: {user_agent}\r\n"
|
||||
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")
|
||||
)
|
||||
|
||||
self.common.log(
|
||||
"UpdateChecker",
|
||||
"check",
|
||||
f"latest OnionShare version: {latest_version}",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self.common.log("UpdateChecker", "check", str(e))
|
||||
self.update_error.emit()
|
||||
raise UpdateCheckerCheckError
|
||||
|
||||
# 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):
|
||||
self.update_invalid_version.emit(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()
|
||||
)
|
||||
# Re-load the settings first before saving, just in case they've changed since we started our thread
|
||||
settings.load()
|
||||
settings.set("autoupdate_timestamp", timestamp)
|
||||
settings.save()
|
||||
|
||||
# Do we need to update?
|
||||
update_url = f"https://github.com/micahflee/onionshare/releases/tag/v{latest_version}"
|
||||
installed_version = self.common.version
|
||||
if installed_version < latest_version:
|
||||
self.update_available.emit(
|
||||
update_url, installed_version, latest_version
|
||||
)
|
||||
return
|
||||
|
||||
# No updates are available
|
||||
self.update_not_available.emit()
|
||||
|
||||
|
||||
class UpdateThread(QtCore.QThread):
|
||||
update_available = QtCore.pyqtSignal(str, str, str)
|
||||
update_not_available = QtCore.pyqtSignal()
|
||||
update_error = QtCore.pyqtSignal()
|
||||
update_invalid_version = QtCore.pyqtSignal(str)
|
||||
|
||||
def __init__(self, common, onion, force=False):
|
||||
super(UpdateThread, self).__init__()
|
||||
|
||||
self.common = common
|
||||
|
||||
self.common.log("UpdateThread", "__init__")
|
||||
self.onion = onion
|
||||
self.force = force
|
||||
|
||||
def run(self):
|
||||
self.common.log("UpdateThread", "run")
|
||||
|
||||
u = UpdateChecker(self.common, self.onion)
|
||||
u.update_available.connect(self._update_available)
|
||||
u.update_not_available.connect(self._update_not_available)
|
||||
u.update_error.connect(self._update_error)
|
||||
u.update_invalid_version.connect(self._update_invalid_version)
|
||||
|
||||
try:
|
||||
u.check(force=self.force)
|
||||
except Exception as e:
|
||||
# If update check fails, silently ignore
|
||||
self.common.log("UpdateThread", "run", str(e))
|
||||
pass
|
||||
|
||||
def _update_available(self, update_url, installed_version, latest_version):
|
||||
self.common.log("UpdateThread", "_update_available")
|
||||
self.active = False
|
||||
self.update_available.emit(update_url, installed_version, latest_version)
|
||||
|
||||
def _update_not_available(self):
|
||||
self.common.log("UpdateThread", "_update_not_available")
|
||||
self.active = False
|
||||
self.update_not_available.emit()
|
||||
|
||||
def _update_error(self):
|
||||
self.common.log("UpdateThread", "_update_error")
|
||||
self.active = False
|
||||
self.update_error.emit()
|
||||
|
||||
def _update_invalid_version(self, latest_version):
|
||||
self.common.log("UpdateThread", "_update_invalid_version")
|
||||
self.active = False
|
||||
self.update_invalid_version.emit(latest_version)
|
Loading…
Add table
Add a link
Reference in a new issue