mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-10 03:37:28 -03:00
Merge branch 'censorship' of github.com:onionshare/onionshare into auto-connect-ui
This commit is contained in:
commit
3422364fa1
35 changed files with 1205 additions and 1104 deletions
|
@ -30,6 +30,10 @@ jobs:
|
|||
command: |
|
||||
cd ~/repo/cli
|
||||
poetry run pytest -v ./tests
|
||||
poetry run onionshare-cli --local-only ./tests --auto-stop-timer 2
|
||||
poetry run onionshare-cli --local-only --receive --auto-stop-timer 2
|
||||
poetry run onionshare-cli --local-only --website ../docs --auto-stop-timer 2
|
||||
poetry run onionshare-cli --local-only --chat --auto-stop-timer 2
|
||||
|
||||
test-gui:
|
||||
docker:
|
||||
|
|
|
@ -74,11 +74,11 @@ git checkout v$VERSION
|
|||
|
||||
You must have `snap` and `snapcraft` (`snap install snapcraft --classic`) installed.
|
||||
|
||||
Build and test the snap before publishing:
|
||||
Build and test the snap before publishing (note that `--dangerous` lets you install the snap before it's codesigned):
|
||||
|
||||
```sh
|
||||
snapcraft
|
||||
snap install --devmode ./onionshare_$VERSION_amd64.snap
|
||||
snap install --dangerous ./onionshare_$VERSION_amd64.snap
|
||||
```
|
||||
|
||||
This will create `onionshare_$VERSION_amd64.snap`.
|
||||
|
|
|
@ -27,8 +27,6 @@ from datetime import datetime
|
|||
from datetime import timedelta
|
||||
|
||||
from .common import Common, CannotFindTor
|
||||
from .censorship import CensorshipCircumvention
|
||||
from .meek import Meek, MeekNotRunning
|
||||
from .web import Web
|
||||
from .onion import TorErrorProtocolError, TorTooOldEphemeral, TorTooOldStealth, Onion
|
||||
from .onionshare import OnionShare
|
||||
|
@ -285,20 +283,6 @@ def main(cwd=None):
|
|||
# Create the Web object
|
||||
web = Web(common, False, mode_settings, mode)
|
||||
|
||||
# Create the Meek object and start the meek client
|
||||
# meek = Meek(common)
|
||||
# meek.start()
|
||||
|
||||
# Create the CensorshipCircumvention object to make
|
||||
# API calls to Tor over Meek
|
||||
censorship = CensorshipCircumvention(common, meek)
|
||||
# Example: request recommended bridges, pretending to be from China, using
|
||||
# domain fronting.
|
||||
# censorship_recommended_settings = censorship.request_settings(country="cn")
|
||||
# print(censorship_recommended_settings)
|
||||
# Clean up the meek subprocess once we're done working with the censorship circumvention API
|
||||
# meek.cleanup()
|
||||
|
||||
# Start the Onion object
|
||||
try:
|
||||
onion = Onion(common, use_tmp_dir=True)
|
||||
|
@ -474,13 +458,13 @@ def main(cwd=None):
|
|||
if app.autostop_timer > 0:
|
||||
# if the auto-stop timer was set and has run out, stop the server
|
||||
if not app.autostop_timer_thread.is_alive():
|
||||
if mode == "share" or (mode == "website"):
|
||||
if mode == "share":
|
||||
# If there were no attempts to download the share, or all downloads are done, we can stop
|
||||
if web.share_mode.cur_history_id == 0 or web.done:
|
||||
print("Stopped because auto-stop timer ran out")
|
||||
web.stop(app.port)
|
||||
break
|
||||
if mode == "receive":
|
||||
elif mode == "receive":
|
||||
if (
|
||||
web.receive_mode.cur_history_id == 0
|
||||
or not web.receive_mode.uploads_in_progress
|
||||
|
@ -489,6 +473,11 @@ def main(cwd=None):
|
|||
web.stop(app.port)
|
||||
break
|
||||
web.receive_mode.can_upload = False
|
||||
else:
|
||||
# website or chat mode
|
||||
print("Stopped because auto-stop timer ran out")
|
||||
web.stop(app.port)
|
||||
break
|
||||
# Allow KeyboardInterrupt exception to be handled with threads
|
||||
# https://stackoverflow.com/questions/3788208/python-threading-ignores-keyboardinterrupt-exception
|
||||
time.sleep(0.2)
|
||||
|
|
|
@ -87,197 +87,206 @@ class Common:
|
|||
╰───────────────────────────────────────────╯
|
||||
"""
|
||||
|
||||
if self.platform == "Windows":
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "╭───────────────────────────────────────────╮"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄▄█████▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄████▀▀▀████▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█▀ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄█▄ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄█████▄ ███"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ███ ▀█████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀██▄ ▀█▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀██▄ ▄█▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀████▄▄▄████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█████▀▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄▀▄ ▄▀▀ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ ▀ ▀▄ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "│ │"
|
||||
)
|
||||
left_spaces = (43 - len(self.version) - 1) // 2
|
||||
right_spaces = left_spaces
|
||||
if left_spaces + len(self.version) + 1 + right_spaces < 43:
|
||||
right_spaces += 1
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ f"{' '*left_spaces}v{self.version}{' '*right_spaces}"
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "│ │"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " https://onionshare.org/ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA + Fore.WHITE + "╰───────────────────────────────────────────╯"
|
||||
)
|
||||
print()
|
||||
try:
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "╭───────────────────────────────────────────╮"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄▄█████▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄████▀▀▀████▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█▀ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▄█▄ ▀██▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄█████▄ ███"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ███ ▀█████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀██▄ ▀█▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀██▄ ▄█▄▄"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ Fore.WHITE
|
||||
+ "▀████▄▄▄████▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█████▀▀ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- * "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▄▀▄ ▄▀▀ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ ▀ ▀▄ █ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│ │"
|
||||
)
|
||||
left_spaces = (43 - len(self.version) - 1) // 2
|
||||
right_spaces = left_spaces
|
||||
if left_spaces + len(self.version) + 1 + right_spaces < 43:
|
||||
right_spaces += 1
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ f"{' '*left_spaces}v{self.version}{' '*right_spaces}"
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│ │"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ Fore.WHITE
|
||||
+ " https://onionshare.org/ "
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
)
|
||||
print(
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "╰───────────────────────────────────────────╯"
|
||||
)
|
||||
print()
|
||||
except:
|
||||
# If anything fails, print a boring banner
|
||||
print(f"OnionShare v{self.version}")
|
||||
print("https://onionshare.org/")
|
||||
print()
|
||||
|
||||
def load_settings(self, config=None):
|
||||
"""
|
||||
|
@ -310,32 +319,15 @@ class Common:
|
|||
|
||||
def get_tor_paths(self):
|
||||
if self.platform == "Linux":
|
||||
# Look in resources first
|
||||
base_path = self.get_resource_path("tor")
|
||||
if os.path.exists(base_path):
|
||||
self.log(
|
||||
"Common", "get_tor_paths", f"using tor binaries in {base_path}"
|
||||
)
|
||||
tor_path = os.path.join(base_path, "tor")
|
||||
tor_geo_ip_file_path = os.path.join(base_path, "geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
|
||||
obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy")
|
||||
snowflake_file_path = os.path.join(base_path, "snowflake-client")
|
||||
meek_client_file_path = os.path.join(base_path, "meek-client")
|
||||
else:
|
||||
# Fallback to looking in the path
|
||||
self.log(
|
||||
"Common", "get_tor_paths", f"using tor binaries in system path"
|
||||
)
|
||||
tor_path = shutil.which("tor")
|
||||
if not tor_path:
|
||||
raise CannotFindTor()
|
||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||
snowflake_file_path = shutil.which("snowflake-client")
|
||||
meek_client_file_path = shutil.which("meek-client")
|
||||
prefix = os.path.dirname(os.path.dirname(tor_path))
|
||||
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6")
|
||||
tor_path = shutil.which("tor")
|
||||
if not tor_path:
|
||||
raise CannotFindTor()
|
||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||
snowflake_file_path = shutil.which("snowflake-client")
|
||||
meek_client_file_path = shutil.which("meek-client")
|
||||
prefix = os.path.dirname(os.path.dirname(tor_path))
|
||||
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6")
|
||||
elif self.platform == "Windows":
|
||||
base_path = self.get_resource_path("tor")
|
||||
tor_path = os.path.join(base_path, "Tor", "tor.exe")
|
||||
|
@ -345,26 +337,15 @@ class Common:
|
|||
tor_geo_ip_file_path = os.path.join(base_path, "Data", "Tor", "geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(base_path, "Data", "Tor", "geoip6")
|
||||
elif self.platform == "Darwin":
|
||||
# Look in resources first
|
||||
base_path = self.get_resource_path("tor")
|
||||
if os.path.exists(base_path):
|
||||
tor_path = os.path.join(base_path, "tor")
|
||||
tor_geo_ip_file_path = os.path.join(base_path, "geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
|
||||
obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy")
|
||||
meek_client_file_path = os.path.join(base_path, "meek-client")
|
||||
snowflake_file_path = os.path.join(base_path, "snowflake-client")
|
||||
else:
|
||||
# Fallback to looking in the path
|
||||
tor_path = shutil.which("tor")
|
||||
if not tor_path:
|
||||
raise CannotFindTor()
|
||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||
snowflake_file_path = shutil.which("snowflake-client")
|
||||
meek_client_file_path = shutil.which("meek-client")
|
||||
prefix = os.path.dirname(os.path.dirname(tor_path))
|
||||
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6")
|
||||
tor_path = shutil.which("tor")
|
||||
if not tor_path:
|
||||
raise CannotFindTor()
|
||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||
snowflake_file_path = shutil.which("snowflake-client")
|
||||
meek_client_file_path = shutil.which("meek-client")
|
||||
prefix = os.path.dirname(os.path.dirname(tor_path))
|
||||
tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6")
|
||||
elif self.platform == "BSD":
|
||||
tor_path = "/usr/local/bin/tor"
|
||||
tor_geo_ip_file_path = "/usr/local/share/tor/geoip"
|
||||
|
@ -460,6 +441,18 @@ class Common:
|
|||
r = random.SystemRandom()
|
||||
return "-".join(r.choice(wordlist) for _ in range(word_count))
|
||||
|
||||
def is_flatpak(self):
|
||||
"""
|
||||
Returns True if OnionShare is running in a Flatpak sandbox
|
||||
"""
|
||||
return os.environ.get("FLATPAK_ID") == "org.onionshare.OnionShare"
|
||||
|
||||
def is_snapcraft(self):
|
||||
"""
|
||||
Returns True if OnionShare is running in a Snapcraft sandbox
|
||||
"""
|
||||
return os.environ.get("SNAP_INSTANCE_NAME") == "onionshare"
|
||||
|
||||
@staticmethod
|
||||
def random_string(num_bytes, output_len=None):
|
||||
"""
|
||||
|
|
|
@ -85,6 +85,10 @@ class Meek(object):
|
|||
self.common.log("Meek", "start", "Starting meek client")
|
||||
|
||||
if self.common.platform == "Windows":
|
||||
env = os.environ.copy()
|
||||
for key in self.meek_env:
|
||||
env[key] = self.meek_env[key]
|
||||
|
||||
# In Windows, hide console window when opening meek-client.exe subprocess
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
|
@ -100,7 +104,7 @@ class Meek(object):
|
|||
stderr=subprocess.PIPE,
|
||||
startupinfo=startupinfo,
|
||||
bufsize=1,
|
||||
env=self.meek_env,
|
||||
env=env,
|
||||
text=True,
|
||||
)
|
||||
else:
|
||||
|
@ -116,7 +120,8 @@ class Meek(object):
|
|||
stderr=subprocess.PIPE,
|
||||
bufsize=1,
|
||||
env=self.meek_env,
|
||||
text=True,
|
||||
# Using universal_newlines instead of text because the snap package using python < 3.7
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
# Queue up the stdout from the subprocess for polling later
|
||||
|
@ -129,6 +134,7 @@ class Meek(object):
|
|||
# read stdout without blocking
|
||||
try:
|
||||
line = q.get_nowait()
|
||||
self.common.log("Meek", "start", line.strip())
|
||||
except Empty:
|
||||
# no stdout yet?
|
||||
pass
|
||||
|
@ -136,10 +142,17 @@ class Meek(object):
|
|||
if "CMETHOD meek socks5" in line:
|
||||
self.meek_host = line.split(" ")[3].split(":")[0]
|
||||
self.meek_port = line.split(" ")[3].split(":")[1]
|
||||
self.common.log("Meek", "start", f"Meek host is {self.meek_host}")
|
||||
self.common.log("Meek", "start", f"Meek port is {self.meek_port}")
|
||||
self.common.log(
|
||||
"Meek",
|
||||
"start",
|
||||
f"Meek running on {self.meek_host}:{self.meek_port}",
|
||||
)
|
||||
break
|
||||
|
||||
if "CMETHOD-ERROR" in line:
|
||||
self.cleanup()
|
||||
raise MeekNotRunning()
|
||||
|
||||
if self.meek_port:
|
||||
self.meek_proxies = {
|
||||
"http": f"socks5h://{self.meek_host}:{self.meek_port}",
|
||||
|
@ -147,6 +160,7 @@ class Meek(object):
|
|||
}
|
||||
else:
|
||||
self.common.log("Meek", "start", "Could not obtain the meek port")
|
||||
self.cleanup()
|
||||
raise MeekNotRunning()
|
||||
|
||||
def cleanup(self):
|
||||
|
|
|
@ -29,6 +29,7 @@ import subprocess
|
|||
import time
|
||||
import shlex
|
||||
import psutil
|
||||
import traceback
|
||||
|
||||
from distutils.version import LooseVersion as Version
|
||||
|
||||
|
@ -200,8 +201,6 @@ class Onion(object):
|
|||
)
|
||||
return
|
||||
|
||||
self.common.log("Onion", "connect")
|
||||
|
||||
# Either use settings that are passed in, or use them from common
|
||||
if custom_settings:
|
||||
self.settings = custom_settings
|
||||
|
@ -212,6 +211,12 @@ class Onion(object):
|
|||
self.common.load_settings()
|
||||
self.settings = self.common.settings
|
||||
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
f"connection_type={self.settings.get('connection_type')}",
|
||||
)
|
||||
|
||||
# The Tor controller
|
||||
self.c = None
|
||||
|
||||
|
@ -315,42 +320,46 @@ class Onion(object):
|
|||
f.write(torrc_template)
|
||||
|
||||
# Bridge support
|
||||
if self.settings.get("tor_bridges_use_obfs4"):
|
||||
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_azure"):
|
||||
with open(
|
||||
self.common.get_resource_path("torrc_template-meek_lite_azure")
|
||||
) as o:
|
||||
for line in o:
|
||||
f.write(line)
|
||||
elif self.settings.get("tor_bridges_use_snowflake"):
|
||||
with open(
|
||||
self.common.get_resource_path("torrc_template-snowflake")
|
||||
) as o:
|
||||
for line in o:
|
||||
f.write(line)
|
||||
if self.settings.get("bridges_enabled"):
|
||||
if self.settings.get("bridges_type") == "built-in":
|
||||
if self.settings.get("bridges_builtin_pt") == "obfs4":
|
||||
with open(
|
||||
self.common.get_resource_path("torrc_template-obfs4")
|
||||
) as o:
|
||||
f.write(o.read())
|
||||
elif self.settings.get("bridges_builtin_pt") == "meek-azure":
|
||||
with open(
|
||||
self.common.get_resource_path(
|
||||
"torrc_template-meek_lite_azure"
|
||||
)
|
||||
) as o:
|
||||
f.write(o.read())
|
||||
elif self.settings.get("bridges_builtin_pt") == "snowflake":
|
||||
with open(
|
||||
self.common.get_resource_path(
|
||||
"torrc_template-snowflake"
|
||||
)
|
||||
) as o:
|
||||
f.write(o.read())
|
||||
|
||||
elif self.settings.get("tor_bridges_use_moat"):
|
||||
for line in self.settings.get("tor_bridges_use_moat_bridges").split(
|
||||
"\n"
|
||||
):
|
||||
if line.strip() != "":
|
||||
f.write(f"Bridge {line}\n")
|
||||
f.write("\nUseBridges 1\n")
|
||||
elif self.settings.get("bridges_type") == "moat":
|
||||
for line in self.settings.get("bridges_moat").split("\n"):
|
||||
if line.strip() != "":
|
||||
f.write(f"Bridge {line}\n")
|
||||
f.write("\nUseBridges 1\n")
|
||||
|
||||
elif self.settings.get("tor_bridges_use_custom_bridges"):
|
||||
for line in self.settings.get(
|
||||
"tor_bridges_use_custom_bridges"
|
||||
).split("\n"):
|
||||
if line.strip() != "":
|
||||
f.write(f"Bridge {line}\n")
|
||||
f.write("\nUseBridges 1\n")
|
||||
elif self.settings.get("bridges_type") == "custom":
|
||||
for line in self.settings.get("bridges_custom").split("\n"):
|
||||
if line.strip() != "":
|
||||
f.write(f"Bridge {line}\n")
|
||||
f.write("\nUseBridges 1\n")
|
||||
|
||||
# Execute a tor subprocess
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
f"starting {self.tor_path} subprocess",
|
||||
)
|
||||
start_ts = time.time()
|
||||
if self.common.platform == "Windows":
|
||||
# In Windows, hide console window when opening tor.exe subprocess
|
||||
|
@ -363,17 +372,32 @@ class Onion(object):
|
|||
startupinfo=startupinfo,
|
||||
)
|
||||
else:
|
||||
if self.common.is_snapcraft():
|
||||
env = None
|
||||
else:
|
||||
env = {"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)}
|
||||
|
||||
self.tor_proc = subprocess.Popen(
|
||||
[self.tor_path, "-f", self.tor_torrc],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env={"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)},
|
||||
env=env,
|
||||
)
|
||||
|
||||
# Wait for the tor controller to start
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
f"tor pid: {self.tor_proc.pid}",
|
||||
)
|
||||
time.sleep(2)
|
||||
|
||||
# Connect to the controller
|
||||
self.common.log(
|
||||
"Onion",
|
||||
"connect",
|
||||
"authenticating to tor controller",
|
||||
)
|
||||
try:
|
||||
if (
|
||||
self.common.platform == "Windows"
|
||||
|
@ -386,6 +410,7 @@ class Onion(object):
|
|||
self.c.authenticate()
|
||||
except Exception as e:
|
||||
print("OnionShare could not connect to Tor:\n{}".format(e.args[0]))
|
||||
print(traceback.format_exc())
|
||||
raise BundledTorBroken(e.args[0])
|
||||
|
||||
while True:
|
||||
|
@ -421,11 +446,7 @@ class Onion(object):
|
|||
time.sleep(0.2)
|
||||
|
||||
# If using bridges, it might take a bit longer to connect to Tor
|
||||
if (
|
||||
self.settings.get("tor_bridges_use_custom_bridges")
|
||||
or self.settings.get("tor_bridges_use_obfs4")
|
||||
or self.settings.get("tor_bridges_use_meek_lite_azure")
|
||||
):
|
||||
if self.settings.get("bridges_enabled"):
|
||||
# Only override timeout if a custom timeout has not been passed in
|
||||
if connect_timeout == 120:
|
||||
connect_timeout = 150
|
||||
|
|
|
@ -11,7 +11,7 @@ function unhumanize(text) {
|
|||
}
|
||||
}
|
||||
function sortTable(n) {
|
||||
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||
var table, rows, switching, i, x, y, valX, valY, shouldSwitch, dir, switchcount = 0;
|
||||
table = document.getElementById("file-list");
|
||||
switching = true;
|
||||
// Set the sorting direction to ascending:
|
||||
|
@ -21,7 +21,7 @@ function sortTable(n) {
|
|||
while (switching) {
|
||||
// Start by saying: no switching is done:
|
||||
switching = false;
|
||||
rows = table.getElementsByTagName("TR");
|
||||
rows = table.getElementsByClassName("row");
|
||||
/* Loop through all table rows (except the
|
||||
first, which contains table headers): */
|
||||
for (i = 1; i < (rows.length - 1); i++) {
|
||||
|
@ -29,18 +29,22 @@ function sortTable(n) {
|
|||
shouldSwitch = false;
|
||||
/* Get the two elements you want to compare,
|
||||
one from current row and one from the next: */
|
||||
x = rows[i].getElementsByTagName("TD")[n];
|
||||
y = rows[i + 1].getElementsByTagName("TD")[n];
|
||||
x = rows[i].getElementsByClassName("cell-data")[n];
|
||||
y = rows[i + 1].getElementsByClassName("cell-data")[n];
|
||||
|
||||
valX = x.classList.contains("size") ? unhumanize(x.innerHTML.toLowerCase()) : x.innerHTML;
|
||||
valY = y.classList.contains("size") ? unhumanize(y.innerHTML.toLowerCase()) : y.innerHTML;
|
||||
|
||||
/* Check if the two rows should switch place,
|
||||
based on the direction, asc or desc: */
|
||||
if (dir == "asc") {
|
||||
if (unhumanize(x.innerHTML.toLowerCase()) > unhumanize(y.innerHTML.toLowerCase())) {
|
||||
// If so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
}
|
||||
if (valX > valY) {
|
||||
// If so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
}
|
||||
} else if (dir == "desc") {
|
||||
if (unhumanize(x.innerHTML.toLowerCase()) < unhumanize(y.innerHTML.toLowerCase())) {
|
||||
if (valX < valY) {
|
||||
// If so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
{% endif %}
|
||||
|
||||
<div class="file-list" id="file-list">
|
||||
<div class="d-flex">
|
||||
<div class="d-flex row">
|
||||
<div id="filename-header" class="heading">Filename</div>
|
||||
<div id="size-header" class="heading">Size</div>
|
||||
</div>
|
||||
|
@ -41,26 +41,26 @@
|
|||
<div>
|
||||
<img width="30" height="30" title="" alt="" src="{{ static_url_path }}/img/web_folder.png" />
|
||||
<a href="{{ info.link }}">
|
||||
<span>{{ info.basename }}</span>
|
||||
<span class="cell-data">{{ info.basename }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>—</div>
|
||||
<div class="cell-data">—</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for info in files %}
|
||||
<div class="d-flex">
|
||||
<div class="d-flex row">
|
||||
<div>
|
||||
<img width="30" height="30" title="" alt="" src="{{ static_url_path }}/img/web_file.png" />
|
||||
{% if download_individual_files %}
|
||||
<a href="{{ info.link }}">
|
||||
<span>{{ info.basename }}</span>
|
||||
<span class="cell-data">{{ info.basename }}</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<span>{{ info.basename }}</span>
|
||||
<span class="cell-data">{{ info.basename }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{{ info.size_human }}</div>
|
||||
<div class="cell-data size">{{ info.size_human }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -106,13 +106,11 @@ class Settings(object):
|
|||
"auto_connect": False,
|
||||
"use_autoupdate": True,
|
||||
"autoupdate_timestamp": None,
|
||||
"no_bridges": True,
|
||||
"tor_bridges_use_obfs4": False,
|
||||
"tor_bridges_use_meek_lite_azure": False,
|
||||
"tor_bridges_use_snowflake": False,
|
||||
"tor_bridges_use_moat": False,
|
||||
"tor_bridges_use_moat_bridges": "",
|
||||
"tor_bridges_use_custom_bridges": "",
|
||||
"bridges_enabled": False,
|
||||
"bridges_type": "built-in", # "built-in", "moat", or "custom"
|
||||
"bridges_builtin_pt": "obfs4", # "obfs4", "meek-azure", or "snowflake"
|
||||
"bridges_moat": "",
|
||||
"bridges_custom": "",
|
||||
"persistent_tabs": [],
|
||||
"locale": None, # this gets defined in fill_in_defaults()
|
||||
"theme": 0,
|
||||
|
|
192
cli/poetry.lock
generated
192
cli/poetry.lock
generated
|
@ -22,12 +22,20 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>
|
|||
|
||||
[[package]]
|
||||
name = "bidict"
|
||||
version = "0.21.3"
|
||||
version = "0.21.4"
|
||||
description = "The bidirectional mapping library for Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "cepa"
|
||||
version = "1.8.3"
|
||||
description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2021.10.8"
|
||||
|
@ -38,7 +46,7 @@ python-versions = "*"
|
|||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.14.6"
|
||||
version = "1.15.0"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -146,7 +154,7 @@ docs = ["sphinx"]
|
|||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.2"
|
||||
version = "3.3"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -154,7 +162,7 @@ python-versions = ">=3.5"
|
|||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "4.8.1"
|
||||
version = "4.8.2"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -167,7 +175,7 @@ zipp = ">=0.5"
|
|||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
perf = ["ipython"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
|
@ -209,14 +217,14 @@ python-versions = ">=3.6"
|
|||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.0"
|
||||
version = "21.2"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
pyparsing = ">=2.0.2,<3"
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
|
@ -246,15 +254,15 @@ test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
|
|||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.20"
|
||||
version = "2.21"
|
||||
description = "C parser in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -316,7 +324,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm
|
|||
|
||||
[[package]]
|
||||
name = "python-engineio"
|
||||
version = "4.2.1"
|
||||
version = "4.3.0"
|
||||
description = "Engine.IO server and client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -328,7 +336,7 @@ client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "python-socketio"
|
||||
version = "5.4.0"
|
||||
version = "5.5.0"
|
||||
description = "Socket.IO server and client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -336,7 +344,7 @@ python-versions = ">=3.6"
|
|||
|
||||
[package.dependencies]
|
||||
bidict = ">=0.21.0"
|
||||
python-engineio = ">=4.1.0"
|
||||
python-engineio = ">=4.3.0"
|
||||
|
||||
[package.extras]
|
||||
asyncio_client = ["aiohttp (>=3.4)"]
|
||||
|
@ -369,21 +377,6 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "stem"
|
||||
version = "1.8.1"
|
||||
description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
develop = false
|
||||
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/onionshare/stem.git"
|
||||
reference = "1.8.1"
|
||||
resolved_reference = "de3d03a03c7ee57c74c80e9c63cb88072d833717"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
|
@ -394,11 +387,11 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
|||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "3.10.0.2"
|
||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||
version = "4.0.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.6+"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "unidecode"
|
||||
|
@ -448,7 +441,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6"
|
||||
content-hash = "181891640e59dac730905019444d42ef8e99da0c34c96fb8a616781661bae537"
|
||||
content-hash = "b6700c9652a3292f2ab3153544c46ed78c75fc9b65c15fcf4e3c243f841565dd"
|
||||
|
||||
[metadata.files]
|
||||
atomicwrites = [
|
||||
|
@ -460,59 +453,67 @@ attrs = [
|
|||
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
|
||||
]
|
||||
bidict = [
|
||||
{file = "bidict-0.21.3-py3-none-any.whl", hash = "sha256:2cce0d01eb3db9b3fa85db501c00aaa3389ee4cab7ef82178604552dfa943a1b"},
|
||||
{file = "bidict-0.21.3.tar.gz", hash = "sha256:d50bd81fae75e34198ffc94979a0eb0939ff9adb3ef32bcc93a913d8b3e3ed1d"},
|
||||
{file = "bidict-0.21.4-py3-none-any.whl", hash = "sha256:3ac67daa353ecf853a1df9d3e924f005e729227a60a8dbada31a4c31aba7f654"},
|
||||
{file = "bidict-0.21.4.tar.gz", hash = "sha256:42c84ffbe6f8de898af6073b4be9ea7ccedcd78d3474aa844c54e49d5a079f6f"},
|
||||
]
|
||||
cepa = [
|
||||
{file = "cepa-1.8.3.tar.gz", hash = "sha256:1dc6f0b324d37a2ed2ca274648ece8fd2c96a1d2f440f58c0ca17afd4b5ede7a"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
|
||||
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
|
||||
]
|
||||
cffi = [
|
||||
{file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
|
||||
{file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
|
||||
{file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"},
|
||||
{file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"},
|
||||
{file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"},
|
||||
{file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"},
|
||||
{file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"},
|
||||
{file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"},
|
||||
{file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"},
|
||||
{file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"},
|
||||
{file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"},
|
||||
{file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"},
|
||||
{file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"},
|
||||
{file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"},
|
||||
{file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"},
|
||||
{file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
|
||||
{file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
|
||||
{file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
|
||||
{file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
|
||||
{file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
|
||||
{file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
|
||||
{file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
|
||||
{file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
|
||||
{file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
|
||||
{file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
|
||||
{file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
|
||||
|
@ -595,12 +596,12 @@ greenlet = [
|
|||
{file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
|
||||
{file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
|
||||
{file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
|
||||
{file = "importlib_metadata-4.8.2-py3-none-any.whl", hash = "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100"},
|
||||
{file = "importlib_metadata-4.8.2.tar.gz", hash = "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
|
@ -671,8 +672,8 @@ markupsafe = [
|
|||
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
|
||||
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
|
||||
{file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"},
|
||||
{file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
|
@ -709,12 +710,12 @@ psutil = [
|
|||
{file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
|
||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pycparser = [
|
||||
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
||||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||
]
|
||||
pynacl = [
|
||||
{file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"},
|
||||
|
@ -750,12 +751,12 @@ pytest = [
|
|||
{file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
|
||||
]
|
||||
python-engineio = [
|
||||
{file = "python-engineio-4.2.1.tar.gz", hash = "sha256:d510329b6d8ed5662547862f58bc73659ae62defa66b66d745ba021de112fa62"},
|
||||
{file = "python_engineio-4.2.1-py3-none-any.whl", hash = "sha256:f3ef9a2c048d08990f294c5f8991f6f162c3b12ecbd368baa0d90441de907d1c"},
|
||||
{file = "python-engineio-4.3.0.tar.gz", hash = "sha256:fed35eeacfa21f53f1fc05ef0cadd65a50780364da3a2be7650eb92f928fdb11"},
|
||||
{file = "python_engineio-4.3.0-py3-none-any.whl", hash = "sha256:ad06a975f7e14cb3bb7137cbf70fd883804484d29acd58004d1db1e2a7fc0ad3"},
|
||||
]
|
||||
python-socketio = [
|
||||
{file = "python-socketio-5.4.0.tar.gz", hash = "sha256:ca807c9e1f168e96dea412d64dd834fb47c470d27fd83da0504aa4b248ba2544"},
|
||||
{file = "python_socketio-5.4.0-py3-none-any.whl", hash = "sha256:7ed57f6c024abdfeb9b25c74c0c00ffc18da47d903e8d72deecb87584370d1fc"},
|
||||
{file = "python-socketio-5.5.0.tar.gz", hash = "sha256:ce972ea1b82aa1811fa10d30cf0d5c251b9a1558c3d66829b6fe70854bcccf0b"},
|
||||
{file = "python_socketio-5.5.0-py3-none-any.whl", hash = "sha256:ca28a0ff0ca5dd05ec5ba4ee2572fe06b96d6f0bc7df384d8b50fbbc06986134"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
|
||||
|
@ -765,15 +766,12 @@ six = [
|
|||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
stem = []
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
|
||||
{file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
|
||||
{file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
|
||||
{file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"},
|
||||
]
|
||||
unidecode = [
|
||||
{file = "Unidecode-1.3.2-py3-none-any.whl", hash = "sha256:215fe33c9d1c889fa823ccb66df91b02524eb8cc8c9c80f9c5b8129754d27829"},
|
||||
|
|
|
@ -29,7 +29,7 @@ eventlet = "*"
|
|||
setuptools = "*"
|
||||
pynacl = "^1.4.0"
|
||||
colorama = "*"
|
||||
stem = {git = "https://github.com/onionshare/stem.git", rev = "1.8.1"}
|
||||
cepa = "1.8.3"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "*"
|
||||
|
|
|
@ -29,13 +29,11 @@ class TestSettings:
|
|||
"auth_password": "",
|
||||
"use_autoupdate": True,
|
||||
"autoupdate_timestamp": None,
|
||||
"no_bridges": True,
|
||||
"tor_bridges_use_obfs4": False,
|
||||
"tor_bridges_use_meek_lite_azure": False,
|
||||
"tor_bridges_use_snowflake": False,
|
||||
"tor_bridges_use_moat": False,
|
||||
"tor_bridges_use_moat_bridges": "",
|
||||
"tor_bridges_use_custom_bridges": "",
|
||||
"bridges_enabled": False,
|
||||
"bridges_type": "built-in",
|
||||
"bridges_builtin_pt": "obfs4",
|
||||
"bridges_moat": "",
|
||||
"bridges_custom": "",
|
||||
"persistent_tabs": [],
|
||||
"theme": 0,
|
||||
}
|
||||
|
@ -96,10 +94,11 @@ class TestSettings:
|
|||
assert settings_obj.get("use_autoupdate") is True
|
||||
assert settings_obj.get("autoupdate_timestamp") is None
|
||||
assert settings_obj.get("autoupdate_timestamp") is None
|
||||
assert settings_obj.get("no_bridges") is True
|
||||
assert settings_obj.get("tor_bridges_use_obfs4") is False
|
||||
assert settings_obj.get("tor_bridges_use_meek_lite_azure") is False
|
||||
assert settings_obj.get("tor_bridges_use_custom_bridges") == ""
|
||||
assert settings_obj.get("bridges_enabled") is False
|
||||
assert settings_obj.get("bridges_type") == "built-in"
|
||||
assert settings_obj.get("bridges_builtin_pt") == "obfs4"
|
||||
assert settings_obj.get("bridges_moat") == ""
|
||||
assert settings_obj.get("bridges_custom") == ""
|
||||
|
||||
def test_set_version(self, settings_obj):
|
||||
settings_obj.set("version", "CUSTOM_VERSION")
|
||||
|
@ -142,10 +141,10 @@ class TestSettings:
|
|||
|
||||
def test_set_custom_bridge(self, settings_obj):
|
||||
settings_obj.set(
|
||||
"tor_bridges_use_custom_bridges",
|
||||
"bridges_custom",
|
||||
"Bridge 45.3.20.65:9050 21300AD88890A49C429A6CB9959CFD44490A8F6E",
|
||||
)
|
||||
assert (
|
||||
settings_obj._settings["tor_bridges_use_custom_bridges"]
|
||||
settings_obj._settings["bridges_custom"]
|
||||
== "Bridge 45.3.20.65:9050 21300AD88890A49C429A6CB9959CFD44490A8F6E"
|
||||
)
|
||||
|
|
|
@ -65,7 +65,7 @@ python scripts\get-tor-windows.py
|
|||
|
||||
### Compile dependencies
|
||||
|
||||
Install Go. The simplest way to make sure everything works is to install Go by following [these instructions](https://golang.org/doc/install).
|
||||
Install Go. The simplest way to make sure everything works is to install Go by following [these instructions](https://golang.org/doc/install). (In Windows, make sure to install the 32-bit version of Go, such as `go1.17.3.windows-386.msi`.)
|
||||
|
||||
Download and compile `meek-client`:
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import shutil
|
|||
import os
|
||||
import subprocess
|
||||
import inspect
|
||||
import platform
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -46,16 +47,21 @@ def main():
|
|||
root_path = os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||
)
|
||||
dist_path = os.path.join(root_path, "src", "onionshare", "resources", "tor")
|
||||
if platform.system() == "Windows":
|
||||
dist_path = os.path.join(root_path, "src", "onionshare", "resources", "tor", "Tor")
|
||||
bin_filename = "meek-client.exe"
|
||||
else:
|
||||
dist_path = os.path.join(root_path, "src", "onionshare", "resources", "tor")
|
||||
bin_filename = "meek-client"
|
||||
|
||||
bin_path = os.path.expanduser("~/go/bin/meek-client")
|
||||
bin_path = os.path.join(os.path.expanduser("~"), "go", "bin", bin_filename)
|
||||
shutil.copyfile(
|
||||
os.path.join(bin_path),
|
||||
os.path.join(dist_path, "meek-client"),
|
||||
os.path.join(dist_path, bin_filename),
|
||||
)
|
||||
os.chmod(os.path.join(dist_path, "meek-client"), 0o755)
|
||||
os.chmod(os.path.join(dist_path, bin_filename), 0o755)
|
||||
|
||||
print(f"Installed meek-client in {dist_path}")
|
||||
print(f"Installed {bin_filename} in {dist_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -34,10 +34,10 @@ import requests
|
|||
|
||||
|
||||
def main():
|
||||
tarball_url = "https://dist.torproject.org/torbrowser/11.0a9/tor-browser-linux64-11.0a9_en-US.tar.xz"
|
||||
tarball_filename = "tor-browser-linux64-11.0a9_en-US.tar.xz"
|
||||
tarball_url = "https://dist.torproject.org/torbrowser/11.0a10/tor-browser-linux64-11.0a10_en-US.tar.xz"
|
||||
tarball_filename = "tor-browser-linux64-11.0a10_en-US.tar.xz"
|
||||
expected_tarball_sha256 = (
|
||||
"cba4a2120b4f847d1ade637e41e69bd01b2e70b4a13e41fe8e69d0424fcf7ca7"
|
||||
"5d3e2ebc4fb6a10f44624359bc2a5a151a57e8402cbd8563d15f9b2524374f1f"
|
||||
)
|
||||
|
||||
# Build paths
|
||||
|
|
|
@ -34,10 +34,10 @@ import requests
|
|||
|
||||
|
||||
def main():
|
||||
dmg_url = "https://dist.torproject.org/torbrowser/11.0a7/TorBrowser-11.0a7-osx64_en-US.dmg"
|
||||
dmg_filename = "TorBrowser-11.0a7-osx64_en-US.dmg"
|
||||
dmg_url = "https://dist.torproject.org/torbrowser/11.0a10/TorBrowser-11.0a10-osx64_en-US.dmg"
|
||||
dmg_filename = "TorBrowser-11.0a10-osx64_en-US.dmg"
|
||||
expected_dmg_sha256 = (
|
||||
"46594cefa29493150d1c0e1933dd656aafcb6b51ef310d44ac059eed2fd1388e"
|
||||
"c6823a28fd28205437564815f93011ff93b7972da2a8ce16919adfc65909e7b9"
|
||||
)
|
||||
|
||||
# Build paths
|
||||
|
@ -101,6 +101,14 @@ def main():
|
|||
os.path.join(dist_path, "obfs4proxy"),
|
||||
)
|
||||
os.chmod(os.path.join(dist_path, "obfs4proxy"), 0o755)
|
||||
# snowflake-client binary
|
||||
shutil.copyfile(
|
||||
os.path.join(
|
||||
dmg_tor_path, "MacOS", "Tor", "PluggableTransports", "snowflake-client"
|
||||
),
|
||||
os.path.join(dist_path, "snowflake-client"),
|
||||
)
|
||||
os.chmod(os.path.join(dist_path, "snowflake-client"), 0o755)
|
||||
|
||||
# Eject dmg
|
||||
subprocess.call(["diskutil", "eject", "/Volumes/Tor Browser"])
|
||||
|
|
|
@ -33,10 +33,10 @@ import requests
|
|||
|
||||
|
||||
def main():
|
||||
exe_url = "https://dist.torproject.org/torbrowser/11.0a7/torbrowser-install-11.0a7_en-US.exe"
|
||||
exe_filename = "torbrowser-install-11.0a7_en-US.exe"
|
||||
exe_url = "https://dist.torproject.org/torbrowser/11.0a10/torbrowser-install-11.0a10_en-US.exe"
|
||||
exe_filename = "torbrowser-install-11.0a10_en-US.exe"
|
||||
expected_exe_sha256 = (
|
||||
"8b2013669d88e3ae8fa9bc17a3495eaac9475f79a849354e826e5132811a860b"
|
||||
"f567dd8368dea0a8d7bbf7c19ece7840f93d493e70662939b92f5058c8dc8d2d"
|
||||
)
|
||||
# Build paths
|
||||
root_path = os.path.dirname(
|
||||
|
|
|
@ -93,6 +93,7 @@ class GuiCommon:
|
|||
share_zip_progess_bar_chunk_color = "#4E064F"
|
||||
history_background_color = "#ffffff"
|
||||
history_label_color = "#000000"
|
||||
settings_error_color = "#FF0000"
|
||||
if color_mode == "dark":
|
||||
header_color = "#F2F2F2"
|
||||
title_color = "#F2F2F2"
|
||||
|
@ -103,6 +104,7 @@ class GuiCommon:
|
|||
share_zip_progess_bar_border_color = "#F2F2F2"
|
||||
history_background_color = "#191919"
|
||||
history_label_color = "#ffffff"
|
||||
settings_error_color = "#FF9999"
|
||||
|
||||
return {
|
||||
# OnionShareGui styles
|
||||
|
@ -303,6 +305,11 @@ class GuiCommon:
|
|||
QLabel {
|
||||
color: #cc0000;
|
||||
}""",
|
||||
"tor_not_connected_label": """
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
}""",
|
||||
# New tab
|
||||
"new_tab_button_image": """
|
||||
QLabel {
|
||||
|
@ -414,10 +421,12 @@ class GuiCommon:
|
|||
QPushButton {
|
||||
padding: 5px 10px;
|
||||
}""",
|
||||
# Moat dialog
|
||||
"moat_error": """
|
||||
# Tor Settings dialogs
|
||||
"tor_settings_error": """
|
||||
QLabel {
|
||||
color: #990000;
|
||||
color: """
|
||||
+ settings_error_color
|
||||
+ """;
|
||||
}
|
||||
""",
|
||||
}
|
||||
|
@ -425,7 +434,10 @@ class GuiCommon:
|
|||
def get_tor_paths(self):
|
||||
if self.common.platform == "Linux":
|
||||
base_path = self.get_resource_path("tor")
|
||||
if os.path.exists(base_path):
|
||||
if base_path and os.path.isdir(base_path):
|
||||
self.common.log(
|
||||
"GuiCommon", "get_tor_paths", "using paths in resources"
|
||||
)
|
||||
tor_path = os.path.join(base_path, "tor")
|
||||
tor_geo_ip_file_path = os.path.join(base_path, "geoip")
|
||||
tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
|
||||
|
@ -434,6 +446,7 @@ class GuiCommon:
|
|||
meek_client_file_path = os.path.join(base_path, "meek-client")
|
||||
else:
|
||||
# Fallback to looking in the path
|
||||
self.common.log("GuiCommon", "get_tor_paths", "using paths from PATH")
|
||||
tor_path = shutil.which("tor")
|
||||
obfs4proxy_file_path = shutil.which("obfs4proxy")
|
||||
snowflake_file_path = shutil.which("snowflake-client")
|
||||
|
@ -480,7 +493,10 @@ class GuiCommon:
|
|||
"""
|
||||
Returns the absolute path of a resource
|
||||
"""
|
||||
return resource_filename("onionshare", os.path.join("resources", filename))
|
||||
try:
|
||||
return resource_filename("onionshare", os.path.join("resources", filename))
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_translated_tor_error(e):
|
||||
|
|
|
@ -23,10 +23,7 @@ import time
|
|||
from PySide2 import QtCore, QtWidgets, QtGui
|
||||
|
||||
from . import strings
|
||||
from .auto_connect import AutoConnect
|
||||
from .tor_connection_dialog import TorConnectionDialog
|
||||
from .tor_settings_dialog import TorSettingsDialog
|
||||
from .settings_dialog import SettingsDialog
|
||||
from .tor_connection import TorConnectionDialog
|
||||
from .widgets import Alert
|
||||
from .update_checker import UpdateThread
|
||||
from .tab_widget import TabWidget
|
||||
|
@ -261,23 +258,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
# Wait 1ms for the event loop to finish closing the TorConnectionDialog
|
||||
QtCore.QTimer.singleShot(1, self.open_tor_settings)
|
||||
|
||||
def open_tor_settings(self, openner=None):
|
||||
def open_tor_settings(self):
|
||||
"""
|
||||
Open the TorSettingsDialog.
|
||||
Open the TorSettingsTab
|
||||
"""
|
||||
self.common.log("MainWindow", "open_tor_settings")
|
||||
d = TorSettingsDialog(self.common, openner)
|
||||
d.settings_saved.connect(self.settings_have_changed)
|
||||
d.exec_()
|
||||
self.tabs.open_tor_settings_tab()
|
||||
|
||||
def open_settings(self):
|
||||
"""
|
||||
Open the SettingsDialog.
|
||||
Open the SettingsTab
|
||||
"""
|
||||
self.common.log("MainWindow", "open_settings")
|
||||
d = SettingsDialog(self.common)
|
||||
d.settings_saved.connect(self.settings_have_changed)
|
||||
d.exec_()
|
||||
self.tabs.open_settings_tab()
|
||||
|
||||
def settings_have_changed(self):
|
||||
self.common.log("OnionShareGui", "settings_have_changed")
|
||||
|
|
|
@ -26,7 +26,7 @@ import json
|
|||
|
||||
from . import strings
|
||||
from .gui_common import GuiCommon
|
||||
from onionshare_cli.meek import MeekNotFound
|
||||
from onionshare_cli.meek import MeekNotFound, MeekNotRunning
|
||||
|
||||
|
||||
class MoatDialog(QtWidgets.QDialog):
|
||||
|
@ -70,7 +70,7 @@ class MoatDialog(QtWidgets.QDialog):
|
|||
|
||||
# Error label
|
||||
self.error_label = QtWidgets.QLabel()
|
||||
self.error_label.setStyleSheet(self.common.gui.css["moat_error"])
|
||||
self.error_label.setStyleSheet(self.common.gui.css["tor_settings_error"])
|
||||
self.error_label.hide()
|
||||
|
||||
# Buttons
|
||||
|
@ -237,7 +237,13 @@ class MoatThread(QtCore.QThread):
|
|||
try:
|
||||
self.meek.start()
|
||||
except MeekNotFound:
|
||||
self.common.log("MoatThread", "run", f"Could not find the Meek Client")
|
||||
self.common.log("MoatThread", "run", f"Could not find meek-client")
|
||||
self.bridgedb_error.emit()
|
||||
return
|
||||
except MeekNotRunning:
|
||||
self.common.log(
|
||||
"MoatThread", "run", f"Ran meek-client, but there was an error"
|
||||
)
|
||||
self.bridgedb_error.emit()
|
||||
return
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"gui_add_files": "Add Files",
|
||||
"gui_add_folder": "Add Folder",
|
||||
"gui_remove": "Remove",
|
||||
"gui_dragdrop_sandbox_flatpak": "To make the Flatpak sandbox more secure, drag and drop is not supported. Use the Add Files and Add Folder buttons to browse for files instead.",
|
||||
"gui_file_selection_remove_all": "Remove All",
|
||||
"gui_choose_items": "Choose",
|
||||
"gui_share_start_server": "Start sharing",
|
||||
|
@ -75,7 +76,8 @@
|
|||
"gui_settings_bridge_custom_radio_option": "Provide a bridge you learned about from a trusted source",
|
||||
"gui_settings_bridge_custom_placeholder": "type address:port (one per line)",
|
||||
"gui_settings_moat_bridges_invalid": "You have not requested a bridge from torproject.org yet.",
|
||||
"gui_settings_tor_bridges_invalid": "None of the bridges you added work.\nDouble-check them or add others.",
|
||||
"gui_settings_tor_bridges_invalid": "None of the bridges you added work. Double-check them or add others.",
|
||||
"gui_settings_stop_active_tabs_label": "There are services running in some of your tabs.\nYou must stop all services to change your Tor settings.",
|
||||
"gui_settings_button_save": "Save",
|
||||
"gui_settings_button_cancel": "Cancel",
|
||||
"gui_settings_button_help": "Help",
|
||||
|
@ -232,5 +234,6 @@
|
|||
"moat_captcha_reload": "Reload",
|
||||
"moat_bridgedb_error": "Error contacting BridgeDB.",
|
||||
"moat_captcha_error": "The solution is not correct. Please try again.",
|
||||
"moat_solution_empty_error": "You must enter the characters from the image"
|
||||
"moat_solution_empty_error": "You must enter the characters from the image",
|
||||
"mode_tor_not_connected_label": "OnionShare is not connected to the Tor network"
|
||||
}
|
|
@ -19,57 +19,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
"""
|
||||
|
||||
from PySide2 import QtCore, QtWidgets, QtGui
|
||||
from PySide2.QtCore import Slot, Qt
|
||||
from PySide2.QtGui import QPalette, QColor
|
||||
import sys
|
||||
import platform
|
||||
import datetime
|
||||
import re
|
||||
import os
|
||||
from onionshare_cli.settings import Settings
|
||||
from onionshare_cli.onion import (
|
||||
Onion,
|
||||
TorErrorInvalidSetting,
|
||||
TorErrorAutomatic,
|
||||
TorErrorSocketPort,
|
||||
TorErrorSocketFile,
|
||||
TorErrorMissingPassword,
|
||||
TorErrorUnreadableCookieFile,
|
||||
TorErrorAuthError,
|
||||
TorErrorProtocolError,
|
||||
BundledTorTimeout,
|
||||
BundledTorBroken,
|
||||
TorTooOldEphemeral,
|
||||
TorTooOldStealth,
|
||||
PortNotAvailable,
|
||||
)
|
||||
|
||||
from . import strings
|
||||
from .widgets import Alert
|
||||
from .update_checker import UpdateThread
|
||||
from .tor_connection_dialog import TorConnectionDialog
|
||||
from .gui_common import GuiCommon
|
||||
|
||||
|
||||
class SettingsDialog(QtWidgets.QDialog):
|
||||
class SettingsTab(QtWidgets.QWidget):
|
||||
"""
|
||||
Settings dialog.
|
||||
"""
|
||||
|
||||
settings_saved = QtCore.Signal()
|
||||
close_this_tab = QtCore.Signal()
|
||||
|
||||
def __init__(self, common):
|
||||
super(SettingsDialog, self).__init__()
|
||||
def __init__(self, common, tab_id):
|
||||
super(SettingsTab, self).__init__()
|
||||
|
||||
self.common = common
|
||||
|
||||
self.common.log("SettingsDialog", "__init__")
|
||||
|
||||
self.setModal(True)
|
||||
self.setWindowTitle(strings._("gui_settings_window_title"))
|
||||
self.setWindowIcon(QtGui.QIcon(GuiCommon.get_resource_path("images/logo.png")))
|
||||
self.common.log("SettingsTab", "__init__")
|
||||
|
||||
self.system = platform.system()
|
||||
self.tab_id = tab_id
|
||||
|
||||
# Automatic updates options
|
||||
|
||||
|
@ -100,9 +73,16 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
)
|
||||
autoupdate_group.setLayout(autoupdate_group_layout)
|
||||
|
||||
autoupdate_layout = QtWidgets.QHBoxLayout()
|
||||
autoupdate_layout.addStretch()
|
||||
autoupdate_layout.addWidget(autoupdate_group)
|
||||
autoupdate_layout.addStretch()
|
||||
autoupdate_widget = QtWidgets.QWidget()
|
||||
autoupdate_widget.setLayout(autoupdate_layout)
|
||||
|
||||
# Autoupdate is only available for Windows and Mac (Linux updates using package manager)
|
||||
if self.system != "Windows" and self.system != "Darwin":
|
||||
autoupdate_group.hide()
|
||||
autoupdate_widget.hide()
|
||||
|
||||
# Language settings
|
||||
language_label = QtWidgets.QLabel(strings._("gui_settings_language_label"))
|
||||
|
@ -117,6 +97,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
locale = language_names_to_locales[language_name]
|
||||
self.language_combobox.addItem(language_name, locale)
|
||||
language_layout = QtWidgets.QHBoxLayout()
|
||||
language_layout.addStretch()
|
||||
language_layout.addWidget(language_label)
|
||||
language_layout.addWidget(self.language_combobox)
|
||||
language_layout.addStretch()
|
||||
|
@ -131,6 +112,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
]
|
||||
self.theme_combobox.addItems(theme_choices)
|
||||
theme_layout = QtWidgets.QHBoxLayout()
|
||||
theme_layout.addStretch()
|
||||
theme_layout.addWidget(theme_label)
|
||||
theme_layout.addWidget(self.theme_combobox)
|
||||
theme_layout.addStretch()
|
||||
|
@ -139,41 +121,44 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
version_label = QtWidgets.QLabel(
|
||||
strings._("gui_settings_version_label").format(self.common.version)
|
||||
)
|
||||
version_label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||
help_label = QtWidgets.QLabel(strings._("gui_settings_help_label"))
|
||||
help_label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||
help_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
|
||||
help_label.setOpenExternalLinks(True)
|
||||
|
||||
# Buttons
|
||||
self.save_button = QtWidgets.QPushButton(strings._("gui_settings_button_save"))
|
||||
self.save_button.clicked.connect(self.save_clicked)
|
||||
self.cancel_button = QtWidgets.QPushButton(
|
||||
strings._("gui_settings_button_cancel")
|
||||
)
|
||||
self.cancel_button.clicked.connect(self.cancel_clicked)
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(self.save_button)
|
||||
buttons_layout.addWidget(self.cancel_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
# Layout
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(autoupdate_group)
|
||||
if autoupdate_group.isVisible():
|
||||
layout.addStretch()
|
||||
layout.addWidget(autoupdate_widget)
|
||||
if autoupdate_widget.isVisible():
|
||||
layout.addSpacing(20)
|
||||
layout.addLayout(language_layout)
|
||||
layout.addLayout(theme_layout)
|
||||
layout.addSpacing(20)
|
||||
layout.addStretch()
|
||||
layout.addWidget(version_label)
|
||||
layout.addWidget(help_label)
|
||||
layout.addSpacing(20)
|
||||
layout.addLayout(buttons_layout)
|
||||
layout.addStretch()
|
||||
|
||||
self.setLayout(layout)
|
||||
self.cancel_button.setFocus()
|
||||
|
||||
self.reload_settings()
|
||||
|
||||
if self.common.gui.onion.connected_to_tor:
|
||||
self.tor_is_connected()
|
||||
else:
|
||||
self.tor_is_disconnected()
|
||||
|
||||
def reload_settings(self):
|
||||
# Load settings, and fill them in
|
||||
self.old_settings = Settings(self.common)
|
||||
|
@ -199,7 +184,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Check for Updates button clicked. Manually force an update check.
|
||||
"""
|
||||
self.common.log("SettingsDialog", "check_for_updates")
|
||||
self.common.log("SettingsTab", "check_for_updates")
|
||||
# Disable buttons
|
||||
self._disable_buttons()
|
||||
self.common.gui.qtapp.processEvents()
|
||||
|
@ -261,7 +246,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Save button clicked. Save current settings to disk.
|
||||
"""
|
||||
self.common.log("SettingsDialog", "save_clicked")
|
||||
self.common.log("SettingsTab", "save_clicked")
|
||||
|
||||
def changed(s1, s2, keys):
|
||||
"""
|
||||
|
@ -298,33 +283,14 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
|
||||
# Save the new settings
|
||||
settings.save()
|
||||
self.settings_saved.emit()
|
||||
self.close()
|
||||
|
||||
def cancel_clicked(self):
|
||||
"""
|
||||
Cancel button clicked.
|
||||
"""
|
||||
self.common.log("SettingsDialog", "cancel_clicked")
|
||||
if (
|
||||
not self.common.gui.local_only
|
||||
and not self.common.gui.onion.is_authenticated()
|
||||
):
|
||||
Alert(
|
||||
self.common,
|
||||
strings._("gui_tor_connection_canceled"),
|
||||
QtWidgets.QMessageBox.Warning,
|
||||
)
|
||||
sys.exit()
|
||||
else:
|
||||
self.close()
|
||||
self.close_this_tab.emit()
|
||||
|
||||
def help_clicked(self):
|
||||
"""
|
||||
Help button clicked.
|
||||
"""
|
||||
self.common.log("SettingsDialog", "help_clicked")
|
||||
SettingsDialog.open_help()
|
||||
self.common.log("SettingsTab", "help_clicked")
|
||||
SettingsTab.open_help()
|
||||
|
||||
@staticmethod
|
||||
def open_help():
|
||||
|
@ -335,7 +301,7 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Return a Settings object that's full of values from the settings dialog.
|
||||
"""
|
||||
self.common.log("SettingsDialog", "settings_from_fields")
|
||||
self.common.log("SettingsTab", "settings_from_fields")
|
||||
settings = Settings(self.common)
|
||||
settings.load() # To get the last update timestamp
|
||||
|
||||
|
@ -350,8 +316,12 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
|
||||
return settings
|
||||
|
||||
def settings_have_changed(self):
|
||||
# Global settings have changed
|
||||
self.common.log("SettingsTab", "settings_have_changed")
|
||||
|
||||
def _update_autoupdate_timestamp(self, autoupdate_timestamp):
|
||||
self.common.log("SettingsDialog", "_update_autoupdate_timestamp")
|
||||
self.common.log("SettingsTab", "_update_autoupdate_timestamp")
|
||||
|
||||
if autoupdate_timestamp:
|
||||
dt = datetime.datetime.fromtimestamp(autoupdate_timestamp)
|
||||
|
@ -363,18 +333,22 @@ class SettingsDialog(QtWidgets.QDialog):
|
|||
)
|
||||
|
||||
def _disable_buttons(self):
|
||||
self.common.log("SettingsDialog", "_disable_buttons")
|
||||
self.common.log("SettingsTab", "_disable_buttons")
|
||||
|
||||
self.check_for_updates_button.setEnabled(False)
|
||||
self.save_button.setEnabled(False)
|
||||
self.cancel_button.setEnabled(False)
|
||||
|
||||
def _enable_buttons(self):
|
||||
self.common.log("SettingsDialog", "_enable_buttons")
|
||||
self.common.log("SettingsTab", "_enable_buttons")
|
||||
# We can't check for updates if we're still not connected to Tor
|
||||
if not self.common.gui.onion.connected_to_tor:
|
||||
self.check_for_updates_button.setEnabled(False)
|
||||
else:
|
||||
self.check_for_updates_button.setEnabled(True)
|
||||
self.save_button.setEnabled(True)
|
||||
self.cancel_button.setEnabled(True)
|
||||
|
||||
def tor_is_connected(self):
|
||||
self.check_for_updates_button.show()
|
||||
|
||||
def tor_is_disconnected(self):
|
||||
self.check_for_updates_button.hide()
|
|
@ -43,7 +43,11 @@ def load_strings(common, locale_dir):
|
|||
current_locale = common.settings.get("locale")
|
||||
strings = {}
|
||||
for s in translations[default_locale]:
|
||||
if s in translations[current_locale] and translations[current_locale][s] != "":
|
||||
if (
|
||||
current_locale in translations
|
||||
and s in translations[current_locale]
|
||||
and translations[current_locale][s] != ""
|
||||
):
|
||||
strings[s] = translations[current_locale][s]
|
||||
else:
|
||||
strings[s] = translations[default_locale][s]
|
||||
|
|
|
@ -28,7 +28,7 @@ from .mode_settings_widget import ModeSettingsWidget
|
|||
from ..server_status import ServerStatus
|
||||
from ... import strings
|
||||
from ...threads import OnionThread, AutoStartTimer
|
||||
from ...widgets import Alert
|
||||
from ...widgets import Alert, MinimumSizeWidget
|
||||
|
||||
|
||||
class Mode(QtWidgets.QWidget):
|
||||
|
@ -101,6 +101,38 @@ class Mode(QtWidgets.QWidget):
|
|||
self.primary_action = QtWidgets.QWidget()
|
||||
self.primary_action.setLayout(self.primary_action_layout)
|
||||
|
||||
# It's up to the downstream Mode to add stuff to self.content_layout
|
||||
# self.content_layout shows the actual content of the mode
|
||||
# self.tor_not_connected_layout is displayed when Tor isn't connected
|
||||
self.content_layout = QtWidgets.QVBoxLayout()
|
||||
self.content_widget = QtWidgets.QWidget()
|
||||
self.content_widget.setLayout(self.content_layout)
|
||||
|
||||
tor_not_connected_label = QtWidgets.QLabel(
|
||||
strings._("mode_tor_not_connected_label")
|
||||
)
|
||||
tor_not_connected_label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||
tor_not_connected_label.setStyleSheet(
|
||||
self.common.gui.css["tor_not_connected_label"]
|
||||
)
|
||||
self.tor_not_connected_layout = QtWidgets.QVBoxLayout()
|
||||
self.tor_not_connected_layout.addStretch()
|
||||
self.tor_not_connected_layout.addWidget(tor_not_connected_label)
|
||||
self.tor_not_connected_layout.addWidget(MinimumSizeWidget(700, 0))
|
||||
self.tor_not_connected_layout.addStretch()
|
||||
self.tor_not_connected_widget = QtWidgets.QWidget()
|
||||
self.tor_not_connected_widget.setLayout(self.tor_not_connected_layout)
|
||||
|
||||
self.wrapper_layout = QtWidgets.QVBoxLayout()
|
||||
self.wrapper_layout.addWidget(self.content_widget)
|
||||
self.wrapper_layout.addWidget(self.tor_not_connected_widget)
|
||||
self.setLayout(self.wrapper_layout)
|
||||
|
||||
if self.common.gui.onion.connected_to_tor:
|
||||
self.tor_connection_started()
|
||||
else:
|
||||
self.tor_connection_stopped()
|
||||
|
||||
def init(self):
|
||||
"""
|
||||
Add custom initialization here.
|
||||
|
@ -524,3 +556,21 @@ class Mode(QtWidgets.QWidget):
|
|||
Used in both Share and Website modes, so implemented here.
|
||||
"""
|
||||
self.history.cancel(event["data"]["id"])
|
||||
|
||||
def tor_connection_started(self):
|
||||
"""
|
||||
This is called on every Mode when Tor is connected
|
||||
"""
|
||||
self.content_widget.show()
|
||||
self.tor_not_connected_widget.hide()
|
||||
|
||||
def tor_connection_stopped(self):
|
||||
"""
|
||||
This is called on every Mode when Tor is disconnected
|
||||
"""
|
||||
if self.common.gui.local_only:
|
||||
self.tor_connection_started()
|
||||
return
|
||||
|
||||
self.content_widget.hide()
|
||||
self.tor_not_connected_widget.show()
|
||||
|
|
|
@ -98,10 +98,8 @@ class ChatMode(Mode):
|
|||
self.column_layout.addWidget(self.image)
|
||||
self.column_layout.addLayout(self.main_layout)
|
||||
|
||||
# Wrapper layout
|
||||
self.wrapper_layout = QtWidgets.QVBoxLayout()
|
||||
self.wrapper_layout.addLayout(self.column_layout)
|
||||
self.setLayout(self.wrapper_layout)
|
||||
# Content layout
|
||||
self.content_layout.addLayout(self.column_layout)
|
||||
|
||||
def get_type(self):
|
||||
"""
|
||||
|
|
|
@ -185,6 +185,12 @@ class FileList(QtWidgets.QListWidget):
|
|||
"""
|
||||
dragEnterEvent for dragging files and directories into the widget.
|
||||
"""
|
||||
# Drag and drop doesn't work in Flatpak, because of the sandbox
|
||||
if self.common.is_flatpak():
|
||||
Alert(self.common, strings._("gui_dragdrop_sandbox_flatpak").format())
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if event.mimeData().hasUrls:
|
||||
self.setStyleSheet(self.common.gui.css["share_file_list_drag_enter"])
|
||||
count = len(event.mimeData().urls())
|
||||
|
@ -206,6 +212,11 @@ class FileList(QtWidgets.QListWidget):
|
|||
"""
|
||||
dragLeaveEvent for dragging files and directories into the widget.
|
||||
"""
|
||||
# Drag and drop doesn't work in Flatpak, because of the sandbox
|
||||
if self.common.is_flatpak():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
self.setStyleSheet(self.common.gui.css["share_file_list_drag_leave"])
|
||||
self.drop_count.hide()
|
||||
event.accept()
|
||||
|
@ -215,6 +226,11 @@ class FileList(QtWidgets.QListWidget):
|
|||
"""
|
||||
dragMoveEvent for dragging files and directories into the widget.
|
||||
"""
|
||||
# Drag and drop doesn't work in Flatpak, because of the sandbox
|
||||
if self.common.is_flatpak():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if event.mimeData().hasUrls:
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
|
@ -225,6 +241,11 @@ class FileList(QtWidgets.QListWidget):
|
|||
"""
|
||||
dropEvent for dragging files and directories into the widget.
|
||||
"""
|
||||
# Drag and drop doesn't work in Flatpak, because of the sandbox
|
||||
if self.common.is_flatpak():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
if event.mimeData().hasUrls:
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
|
@ -342,8 +363,15 @@ class FileSelection(QtWidgets.QVBoxLayout):
|
|||
self.file_list.files_dropped.connect(self.update)
|
||||
self.file_list.files_updated.connect(self.update)
|
||||
|
||||
# Sandboxes (for masOS, Flatpak, etc.) need separate add files and folders buttons, in
|
||||
# order to use native file selection dialogs
|
||||
if self.common.platform == "Darwin" or self.common.is_flatpak():
|
||||
self.sandbox = True
|
||||
else:
|
||||
self.sandbox = False
|
||||
|
||||
# Buttons
|
||||
if self.common.platform == "Darwin":
|
||||
if self.sandbox:
|
||||
# The macOS sandbox makes it so the Mac version needs separate add files
|
||||
# and folders buttons, in order to use native file selection dialogs
|
||||
self.add_files_button = QtWidgets.QPushButton(strings._("gui_add_files"))
|
||||
|
@ -357,7 +385,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
|
|||
self.remove_button.clicked.connect(self.delete)
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
if self.common.platform == "Darwin":
|
||||
if self.sandbox:
|
||||
button_layout.addWidget(self.add_files_button)
|
||||
button_layout.addWidget(self.add_folder_button)
|
||||
else:
|
||||
|
@ -376,14 +404,14 @@ class FileSelection(QtWidgets.QVBoxLayout):
|
|||
"""
|
||||
# All buttons should be hidden if the server is on
|
||||
if self.server_on:
|
||||
if self.common.platform == "Darwin":
|
||||
if self.sandbox:
|
||||
self.add_files_button.hide()
|
||||
self.add_folder_button.hide()
|
||||
else:
|
||||
self.add_button.hide()
|
||||
self.remove_button.hide()
|
||||
else:
|
||||
if self.common.platform == "Darwin":
|
||||
if self.sandbox:
|
||||
self.add_files_button.show()
|
||||
self.add_folder_button.show()
|
||||
else:
|
||||
|
@ -407,6 +435,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
|
|||
"""
|
||||
file_dialog = AddFileDialog(self.common, caption=strings._("gui_choose_items"))
|
||||
if file_dialog.exec_() == QtWidgets.QDialog.Accepted:
|
||||
self.common.log("FileSelection", "add", file_dialog.selectedFiles())
|
||||
for filename in file_dialog.selectedFiles():
|
||||
self.file_list.add_file(filename)
|
||||
|
||||
|
@ -415,25 +444,34 @@ class FileSelection(QtWidgets.QVBoxLayout):
|
|||
|
||||
def add_files(self):
|
||||
"""
|
||||
Add files button clicked.
|
||||
Add Files button clicked.
|
||||
"""
|
||||
files = QtWidgets.QFileDialog.getOpenFileNames(
|
||||
self.parent, caption=strings._("gui_choose_items")
|
||||
)
|
||||
self.common.log("FileSelection", "add_files", files)
|
||||
|
||||
filenames = files[0]
|
||||
for filename in filenames:
|
||||
self.file_list.add_file(filename)
|
||||
|
||||
self.file_list.setCurrentItem(None)
|
||||
self.update()
|
||||
|
||||
def add_folder(self):
|
||||
"""
|
||||
Add folder button clicked.
|
||||
Add Folder button clicked.
|
||||
"""
|
||||
filename = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self.parent,
|
||||
caption=strings._("gui_choose_items"),
|
||||
options=QtWidgets.QFileDialog.ShowDirsOnly,
|
||||
)
|
||||
self.file_list.add_file(filename)
|
||||
self.common.log("FileSelection", "add_folder", filename)
|
||||
if filename:
|
||||
self.file_list.add_file(filename)
|
||||
self.file_list.setCurrentItem(None)
|
||||
self.update()
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
|
|
|
@ -198,10 +198,8 @@ class ReceiveMode(Mode):
|
|||
self.column_layout.addLayout(row_layout)
|
||||
self.column_layout.addWidget(self.history, stretch=1)
|
||||
|
||||
# Wrapper layout
|
||||
self.wrapper_layout = QtWidgets.QVBoxLayout()
|
||||
self.wrapper_layout.addLayout(self.column_layout)
|
||||
self.setLayout(self.wrapper_layout)
|
||||
# Content layout
|
||||
self.content_layout.addLayout(self.column_layout)
|
||||
|
||||
def get_type(self):
|
||||
"""
|
||||
|
|
|
@ -169,10 +169,8 @@ class ShareMode(Mode):
|
|||
self.column_layout.addLayout(self.main_layout)
|
||||
self.column_layout.addWidget(self.history, stretch=1)
|
||||
|
||||
# Wrapper layout
|
||||
self.wrapper_layout = QtWidgets.QVBoxLayout()
|
||||
self.wrapper_layout.addLayout(self.column_layout)
|
||||
self.setLayout(self.wrapper_layout)
|
||||
# Content layout
|
||||
self.content_layout.addLayout(self.column_layout)
|
||||
|
||||
# Always start with focus on file selection
|
||||
self.file_selection.setFocus()
|
||||
|
|
|
@ -167,10 +167,8 @@ class WebsiteMode(Mode):
|
|||
self.column_layout.addLayout(self.main_layout)
|
||||
self.column_layout.addWidget(self.history, stretch=1)
|
||||
|
||||
# Wrapper layout
|
||||
self.wrapper_layout = QtWidgets.QVBoxLayout()
|
||||
self.wrapper_layout.addLayout(self.column_layout)
|
||||
self.setLayout(self.wrapper_layout)
|
||||
# Content layout
|
||||
self.content_layout.addLayout(self.column_layout)
|
||||
|
||||
# Always start with focus on file selection
|
||||
self.file_selection.setFocus()
|
||||
|
|
|
@ -96,7 +96,6 @@ class Tab(QtWidgets.QWidget):
|
|||
tab_id,
|
||||
system_tray,
|
||||
status_bar,
|
||||
mode_settings=None,
|
||||
filenames=None,
|
||||
):
|
||||
super(Tab, self).__init__()
|
||||
|
@ -243,6 +242,8 @@ class Tab(QtWidgets.QWidget):
|
|||
elif mode == "website":
|
||||
self.filenames = self.settings.get("website", "filenames")
|
||||
self.website_mode_clicked()
|
||||
elif mode == "chat":
|
||||
self.chat_mode_clicked()
|
||||
else:
|
||||
# This is a new tab
|
||||
self.settings = ModeSettings(self.common)
|
||||
|
|
|
@ -26,6 +26,8 @@ from . import strings
|
|||
from .tab import Tab
|
||||
from .threads import EventHandlerThread
|
||||
from .gui_common import GuiCommon
|
||||
from .tor_settings_tab import TorSettingsTab
|
||||
from .settings_tab import SettingsTab
|
||||
|
||||
|
||||
class TabWidget(QtWidgets.QTabWidget):
|
||||
|
@ -43,9 +45,12 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
self.system_tray = system_tray
|
||||
self.status_bar = status_bar
|
||||
|
||||
# Keep track of tabs in a dictionary
|
||||
# Keep track of tabs in a dictionary that maps tab_id to tab.
|
||||
# Each tab has a unique, auto-incremented id (tab_id). This is different than the
|
||||
# tab's index, which changes as tabs are re-arranged.
|
||||
self.tabs = {}
|
||||
self.current_tab_id = 0 # Each tab has a unique id
|
||||
self.tor_settings_tab = None
|
||||
|
||||
# Define the new tab button
|
||||
self.new_tab_button = QtWidgets.QPushButton("+", parent=self)
|
||||
|
@ -89,9 +94,12 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
self.event_handler_t.wait(50)
|
||||
|
||||
# Clean up each tab
|
||||
for index in range(self.count()):
|
||||
tab = self.widget(index)
|
||||
tab.cleanup()
|
||||
for tab_id in self.tabs:
|
||||
if not (
|
||||
type(self.tabs[tab_id]) is SettingsTab
|
||||
or type(self.tabs[tab_id]) is TorSettingsTab
|
||||
):
|
||||
self.tabs[tab_id].cleanup()
|
||||
|
||||
def move_new_tab_button(self):
|
||||
# Find the width of all tabs
|
||||
|
@ -114,8 +122,28 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
|
||||
def tab_changed(self):
|
||||
# Active tab was changed
|
||||
tab_id = self.currentIndex()
|
||||
tab = self.widget(self.currentIndex())
|
||||
if not tab:
|
||||
self.common.log(
|
||||
"TabWidget",
|
||||
"tab_changed",
|
||||
f"tab at index {self.currentIndex()} does not exist",
|
||||
)
|
||||
return
|
||||
|
||||
tab_id = tab.tab_id
|
||||
self.common.log("TabWidget", "tab_changed", f"Tab was changed to {tab_id}")
|
||||
|
||||
# If it's Settings or Tor Settings, ignore
|
||||
if (
|
||||
type(self.tabs[tab_id]) is SettingsTab
|
||||
or type(self.tabs[tab_id]) is TorSettingsTab
|
||||
):
|
||||
# Blank the server status indicator
|
||||
self.status_bar.server_status_image_label.clear()
|
||||
self.status_bar.server_status_label.clear()
|
||||
return
|
||||
|
||||
try:
|
||||
mode = self.tabs[tab_id].get_mode()
|
||||
if mode:
|
||||
|
@ -158,23 +186,6 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
index = self.addTab(tab, strings._("gui_new_tab"))
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
# In macOS, manually create a close button because tabs don't seem to have them otherwise
|
||||
if self.common.platform == "Darwin":
|
||||
|
||||
def close_tab():
|
||||
self.tabBar().tabCloseRequested.emit(self.indexOf(tab))
|
||||
|
||||
tab.close_button = QtWidgets.QPushButton()
|
||||
tab.close_button.setFlat(True)
|
||||
tab.close_button.setFixedWidth(40)
|
||||
tab.close_button.setIcon(
|
||||
QtGui.QIcon(GuiCommon.get_resource_path("images/close_tab.png"))
|
||||
)
|
||||
tab.close_button.clicked.connect(close_tab)
|
||||
self.tabBar().setTabButton(
|
||||
index, QtWidgets.QTabBar.RightSide, tab.close_button
|
||||
)
|
||||
|
||||
tab.init(mode_settings)
|
||||
|
||||
# Make sure the title is set
|
||||
|
@ -187,6 +198,44 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
# Bring the window to front, in case this is being added by an event
|
||||
self.bring_to_front.emit()
|
||||
|
||||
def open_settings_tab(self):
|
||||
self.common.log("TabWidget", "open_settings_tab")
|
||||
|
||||
# See if a settings tab is already open, and if so switch to it
|
||||
for tab_id in self.tabs:
|
||||
if type(self.tabs[tab_id]) is SettingsTab:
|
||||
self.setCurrentIndex(self.indexOf(self.tabs[tab_id]))
|
||||
return
|
||||
|
||||
settings_tab = SettingsTab(self.common, self.current_tab_id)
|
||||
settings_tab.close_this_tab.connect(self.close_settings_tab)
|
||||
self.tabs[self.current_tab_id] = settings_tab
|
||||
self.current_tab_id += 1
|
||||
index = self.addTab(settings_tab, strings._("gui_settings_window_title"))
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
def open_tor_settings_tab(self):
|
||||
self.common.log("TabWidget", "open_tor_settings_tab")
|
||||
|
||||
# See if a settings tab is already open, and if so switch to it
|
||||
for tab_id in self.tabs:
|
||||
if type(self.tabs[tab_id]) is TorSettingsTab:
|
||||
self.setCurrentIndex(self.indexOf(self.tabs[tab_id]))
|
||||
return
|
||||
|
||||
self.tor_settings_tab = TorSettingsTab(
|
||||
self.common, self.current_tab_id, self.are_tabs_active(), self.status_bar
|
||||
)
|
||||
self.tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab)
|
||||
self.tor_settings_tab.tor_is_connected.connect(self.tor_is_connected)
|
||||
self.tor_settings_tab.tor_is_disconnected.connect(self.tor_is_disconnected)
|
||||
self.tabs[self.current_tab_id] = self.tor_settings_tab
|
||||
self.current_tab_id += 1
|
||||
index = self.addTab(
|
||||
self.tor_settings_tab, strings._("gui_tor_settings_window_title")
|
||||
)
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
def change_title(self, tab_id, title):
|
||||
shortened_title = title
|
||||
if len(shortened_title) > 11:
|
||||
|
@ -200,6 +249,11 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
index = self.indexOf(self.tabs[tab_id])
|
||||
self.setTabIcon(index, QtGui.QIcon(GuiCommon.get_resource_path(icon_path)))
|
||||
|
||||
# The icon changes when the server status changes, so if we have an open
|
||||
# Tor Settings tab, tell it to update
|
||||
if self.tor_settings_tab:
|
||||
self.tor_settings_tab.active_tabs_changed(self.are_tabs_active())
|
||||
|
||||
def change_persistent(self, tab_id, is_persistent):
|
||||
self.common.log(
|
||||
"TabWidget",
|
||||
|
@ -223,10 +277,14 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
def save_persistent_tabs(self):
|
||||
# Figure out the order of persistent tabs to save in settings
|
||||
persistent_tabs = []
|
||||
for index in range(self.count()):
|
||||
tab = self.widget(index)
|
||||
if tab.settings.get("persistent", "enabled"):
|
||||
persistent_tabs.append(tab.settings.id)
|
||||
for tab_id in self.tabs:
|
||||
if not (
|
||||
type(self.tabs[tab_id]) is SettingsTab
|
||||
or type(self.tabs[tab_id]) is TorSettingsTab
|
||||
):
|
||||
tab = self.widget(self.indexOf(self.tabs[tab_id]))
|
||||
if tab.settings.get("persistent", "enabled"):
|
||||
persistent_tabs.append(tab.settings.id)
|
||||
# Only save if tabs have actually moved
|
||||
if persistent_tabs != self.common.settings.get("persistent_tabs"):
|
||||
self.common.settings.set("persistent_tabs", persistent_tabs)
|
||||
|
@ -235,10 +293,16 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
def close_tab(self, index):
|
||||
self.common.log("TabWidget", "close_tab", f"{index}")
|
||||
tab = self.widget(index)
|
||||
if tab.close_tab():
|
||||
# If the tab is persistent, delete the settings file from disk
|
||||
if tab.settings.get("persistent", "enabled"):
|
||||
tab.settings.delete()
|
||||
tab_id = tab.tab_id
|
||||
|
||||
if (
|
||||
type(self.tabs[tab_id]) is SettingsTab
|
||||
or type(self.tabs[tab_id]) is TorSettingsTab
|
||||
):
|
||||
self.common.log("TabWidget", "closing a settings tab")
|
||||
|
||||
if type(self.tabs[tab_id]) is TorSettingsTab:
|
||||
self.tor_settings_tab = None
|
||||
|
||||
# Remove the tab
|
||||
self.removeTab(index)
|
||||
|
@ -248,17 +312,56 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
if self.count() == 0:
|
||||
self.new_tab_clicked()
|
||||
|
||||
self.save_persistent_tabs()
|
||||
else:
|
||||
self.common.log("TabWidget", "closing a service tab")
|
||||
if tab.close_tab():
|
||||
self.common.log("TabWidget", "user is okay with closing the tab")
|
||||
|
||||
# If the tab is persistent, delete the settings file from disk
|
||||
if tab.settings.get("persistent", "enabled"):
|
||||
tab.settings.delete()
|
||||
|
||||
self.save_persistent_tabs()
|
||||
|
||||
# Remove the tab
|
||||
self.removeTab(index)
|
||||
del self.tabs[tab.tab_id]
|
||||
|
||||
# If the last tab is closed, open a new one
|
||||
if self.count() == 0:
|
||||
self.new_tab_clicked()
|
||||
else:
|
||||
self.common.log("TabWidget", "user does not want to close the tab")
|
||||
|
||||
def close_settings_tab(self):
|
||||
self.common.log("TabWidget", "close_settings_tab")
|
||||
for tab_id in self.tabs:
|
||||
if type(self.tabs[tab_id]) is SettingsTab:
|
||||
index = self.indexOf(self.tabs[tab_id])
|
||||
self.close_tab(index)
|
||||
return
|
||||
|
||||
def close_tor_settings_tab(self):
|
||||
self.common.log("TabWidget", "close_tor_settings_tab")
|
||||
for tab_id in self.tabs:
|
||||
if type(self.tabs[tab_id]) is TorSettingsTab:
|
||||
index = self.indexOf(self.tabs[tab_id])
|
||||
self.close_tab(index)
|
||||
return
|
||||
|
||||
def are_tabs_active(self):
|
||||
"""
|
||||
See if there are active servers in any open tabs
|
||||
"""
|
||||
for tab_id in self.tabs:
|
||||
mode = self.tabs[tab_id].get_mode()
|
||||
if mode:
|
||||
if mode.server_status.status != mode.server_status.STATUS_STOPPED:
|
||||
return True
|
||||
if not (
|
||||
type(self.tabs[tab_id]) is SettingsTab
|
||||
or type(self.tabs[tab_id]) is TorSettingsTab
|
||||
):
|
||||
mode = self.tabs[tab_id].get_mode()
|
||||
if mode:
|
||||
if mode.server_status.status != mode.server_status.STATUS_STOPPED:
|
||||
return True
|
||||
return False
|
||||
|
||||
def paintEvent(self, event):
|
||||
|
@ -273,6 +376,26 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||
super(TabWidget, self).resizeEvent(event)
|
||||
self.move_new_tab_button()
|
||||
|
||||
def tor_is_connected(self):
|
||||
for tab_id in self.tabs:
|
||||
if type(self.tabs[tab_id]) is SettingsTab:
|
||||
self.tabs[tab_id].tor_is_connected()
|
||||
else:
|
||||
if not type(self.tabs[tab_id]) is TorSettingsTab:
|
||||
mode = self.tabs[tab_id].get_mode()
|
||||
if mode:
|
||||
mode.tor_connection_started()
|
||||
|
||||
def tor_is_disconnected(self):
|
||||
for tab_id in self.tabs:
|
||||
if type(self.tabs[tab_id]) is SettingsTab:
|
||||
self.tabs[tab_id].tor_is_disconnected()
|
||||
else:
|
||||
if not type(self.tabs[tab_id]) is TorSettingsTab:
|
||||
mode = self.tabs[tab_id].get_mode()
|
||||
if mode:
|
||||
mode.tor_connection_stopped()
|
||||
|
||||
|
||||
class TabBar(QtWidgets.QTabBar):
|
||||
"""
|
||||
|
|
|
@ -117,7 +117,6 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
|
|||
def _connected_to_tor(self):
|
||||
self.common.log("TorConnectionDialog", "_connected_to_tor")
|
||||
self.active = False
|
||||
|
||||
# Close the dialog after connecting
|
||||
self.setValue(self.maximum())
|
||||
|
||||
|
@ -157,26 +156,136 @@ class TorConnectionDialog(QtWidgets.QProgressDialog):
|
|||
QtCore.QTimer.singleShot(1, self.cancel)
|
||||
|
||||
|
||||
class TorConnectionWidget(QtWidgets.QWidget):
|
||||
"""
|
||||
Connecting to Tor widget, with a progress bar
|
||||
"""
|
||||
|
||||
open_tor_settings = QtCore.Signal()
|
||||
success = QtCore.Signal()
|
||||
fail = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, common, status_bar):
|
||||
super(TorConnectionWidget, self).__init__(None)
|
||||
self.common = common
|
||||
self.common.log("TorConnectionWidget", "__init__")
|
||||
|
||||
self.status_bar = status_bar
|
||||
self.label = QtWidgets.QLabel(strings._("connecting_to_tor"))
|
||||
self.label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||
|
||||
self.progress = QtWidgets.QProgressBar()
|
||||
self.progress.setRange(0, 100)
|
||||
self.cancel_button = QtWidgets.QPushButton(
|
||||
strings._("gui_settings_button_cancel")
|
||||
)
|
||||
self.cancel_button.clicked.connect(self.cancel_clicked)
|
||||
|
||||
progress_layout = QtWidgets.QHBoxLayout()
|
||||
progress_layout.addWidget(self.progress)
|
||||
progress_layout.addWidget(self.cancel_button)
|
||||
|
||||
inner_layout = QtWidgets.QVBoxLayout()
|
||||
inner_layout.addWidget(self.label)
|
||||
inner_layout.addLayout(progress_layout)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout()
|
||||
layout.addStretch()
|
||||
layout.addLayout(inner_layout)
|
||||
layout.addStretch()
|
||||
self.setLayout(layout)
|
||||
|
||||
# Start displaying the status at 0
|
||||
self._tor_status_update(0, "")
|
||||
|
||||
def start(self, custom_settings=False, testing_settings=False, onion=None):
|
||||
self.common.log("TorConnectionWidget", "start")
|
||||
self.was_canceled = False
|
||||
|
||||
self.testing_settings = testing_settings
|
||||
|
||||
if custom_settings:
|
||||
self.settings = custom_settings
|
||||
else:
|
||||
self.settings = self.common.settings
|
||||
|
||||
if self.testing_settings:
|
||||
self.onion = onion
|
||||
else:
|
||||
self.onion = self.common.gui.onion
|
||||
|
||||
t = TorConnectionThread(self.common, self.settings, self)
|
||||
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)
|
||||
t.error_connecting_to_tor.connect(self._error_connecting_to_tor)
|
||||
t.start()
|
||||
|
||||
# The main thread needs to remain active, and checking for Qt events,
|
||||
# until the thread is finished. Otherwise it won't be able to handle
|
||||
# accepting signals.
|
||||
self.active = True
|
||||
while self.active:
|
||||
time.sleep(0.1)
|
||||
self.common.gui.qtapp.processEvents()
|
||||
|
||||
def cancel_clicked(self):
|
||||
self.was_canceled = True
|
||||
self.fail.emit("")
|
||||
|
||||
def wasCanceled(self):
|
||||
return self.was_canceled
|
||||
|
||||
def _tor_status_update(self, progress, summary):
|
||||
self.progress.setValue(int(progress))
|
||||
self.label.setText(
|
||||
f"<strong>{strings._('connecting_to_tor')}</strong><br>{summary}"
|
||||
)
|
||||
|
||||
def _connected_to_tor(self):
|
||||
self.common.log("TorConnectionWidget", "_connected_to_tor")
|
||||
self.active = False
|
||||
self.status_bar.clearMessage()
|
||||
|
||||
# Close the dialog after connecting
|
||||
self.progress.setValue(self.progress.maximum())
|
||||
|
||||
self.success.emit()
|
||||
|
||||
def _canceled_connecting_to_tor(self):
|
||||
self.common.log("TorConnectionWidget", "_canceled_connecting_to_tor")
|
||||
self.active = False
|
||||
self.onion.cleanup()
|
||||
|
||||
# Cancel connecting to Tor
|
||||
QtCore.QTimer.singleShot(1, self.cancel_clicked)
|
||||
|
||||
def _error_connecting_to_tor(self, msg):
|
||||
self.common.log("TorConnectionWidget", "_error_connecting_to_tor")
|
||||
self.active = False
|
||||
self.fail.emit(msg)
|
||||
|
||||
|
||||
class TorConnectionThread(QtCore.QThread):
|
||||
tor_status_update = QtCore.Signal(str, str)
|
||||
connected_to_tor = QtCore.Signal()
|
||||
canceled_connecting_to_tor = QtCore.Signal()
|
||||
error_connecting_to_tor = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, common, settings, dialog):
|
||||
def __init__(self, common, settings, parent):
|
||||
super(TorConnectionThread, self).__init__()
|
||||
self.common = common
|
||||
self.common.log("TorConnectionThread", "__init__")
|
||||
self.settings = settings
|
||||
self.dialog = dialog
|
||||
self.parent = parent
|
||||
|
||||
def run(self):
|
||||
self.common.log("TorConnectionThread", "run")
|
||||
|
||||
# Connect to the Onion
|
||||
try:
|
||||
self.dialog.onion.connect(self.settings, False, self._tor_status_update)
|
||||
if self.dialog.onion.connected_to_tor:
|
||||
self.parent.onion.connect(self.settings, False, self._tor_status_update)
|
||||
if self.parent.onion.connected_to_tor:
|
||||
self.connected_to_tor.emit()
|
||||
else:
|
||||
self.canceled_connecting_to_tor.emit()
|
||||
|
@ -212,4 +321,4 @@ class TorConnectionThread(QtCore.QThread):
|
|||
self.tor_status_update.emit(progress, summary)
|
||||
|
||||
# Return False if the dialog was canceled
|
||||
return not self.dialog.wasCanceled()
|
||||
return not self.parent.wasCanceled()
|
|
@ -30,33 +30,30 @@ from onionshare_cli.onion import Onion
|
|||
|
||||
from . import strings
|
||||
from .widgets import Alert
|
||||
from .tor_connection_dialog import TorConnectionDialog
|
||||
from .tor_connection import TorConnectionWidget
|
||||
from .moat_dialog import MoatDialog
|
||||
from .gui_common import GuiCommon
|
||||
|
||||
|
||||
class TorSettingsDialog(QtWidgets.QDialog):
|
||||
class TorSettingsTab(QtWidgets.QWidget):
|
||||
"""
|
||||
Settings dialog.
|
||||
"""
|
||||
|
||||
settings_saved = QtCore.Signal()
|
||||
close_this_tab = QtCore.Signal()
|
||||
tor_is_connected = QtCore.Signal()
|
||||
tor_is_disconnected = QtCore.Signal()
|
||||
|
||||
def __init__(self, common, openner=None):
|
||||
super(TorSettingsDialog, self).__init__()
|
||||
def __init__(self, common, tab_id, are_tabs_active, status_bar):
|
||||
super(TorSettingsTab, self).__init__()
|
||||
|
||||
self.common = common
|
||||
self.openner = openner
|
||||
|
||||
self.common.log("TorSettingsDialog", "__init__")
|
||||
self.common.log("TorSettingsTab", "__init__")
|
||||
|
||||
self.status_bar = status_bar
|
||||
self.meek = Meek(common, get_tor_paths=self.common.gui.get_tor_paths)
|
||||
|
||||
self.setModal(True)
|
||||
self.setWindowTitle(strings._("gui_tor_settings_window_title"))
|
||||
self.setWindowIcon(QtGui.QIcon(GuiCommon.get_resource_path("images/logo.png")))
|
||||
|
||||
self.system = platform.system()
|
||||
self.tab_id = tab_id
|
||||
|
||||
# Connection type: either automatic, control port, or socket file
|
||||
|
||||
|
@ -300,6 +297,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
connection_type_radio_group_layout.addWidget(
|
||||
self.connection_type_socket_file_radio
|
||||
)
|
||||
connection_type_radio_group_layout.addStretch()
|
||||
connection_type_radio_group = QtWidgets.QGroupBox(
|
||||
strings._("gui_settings_connection_type_label")
|
||||
)
|
||||
|
@ -320,6 +318,28 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
connection_type_layout = QtWidgets.QVBoxLayout()
|
||||
connection_type_layout.addWidget(self.tor_settings_group)
|
||||
connection_type_layout.addWidget(self.connection_type_bridges_radio_group)
|
||||
connection_type_layout.addStretch()
|
||||
|
||||
# Settings are in columns
|
||||
columns_layout = QtWidgets.QHBoxLayout()
|
||||
columns_layout.addWidget(connection_type_radio_group)
|
||||
columns_layout.addSpacing(20)
|
||||
columns_layout.addLayout(connection_type_layout, stretch=1)
|
||||
columns_wrapper = QtWidgets.QWidget()
|
||||
columns_wrapper.setFixedHeight(400)
|
||||
columns_wrapper.setLayout(columns_layout)
|
||||
|
||||
# Tor connection widget
|
||||
self.tor_con = TorConnectionWidget(self.common, self.status_bar)
|
||||
self.tor_con.success.connect(self.tor_con_success)
|
||||
self.tor_con.fail.connect(self.tor_con_fail)
|
||||
self.tor_con.hide()
|
||||
self.tor_con_type = None
|
||||
|
||||
# Error label
|
||||
self.error_label = QtWidgets.QLabel()
|
||||
self.error_label.setStyleSheet(self.common.gui.css["tor_settings_error"])
|
||||
self.error_label.setWordWrap(True)
|
||||
|
||||
# Buttons
|
||||
self.test_tor_button = QtWidgets.QPushButton(
|
||||
|
@ -328,26 +348,42 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
self.test_tor_button.clicked.connect(self.test_tor_clicked)
|
||||
self.save_button = QtWidgets.QPushButton(strings._("gui_settings_button_save"))
|
||||
self.save_button.clicked.connect(self.save_clicked)
|
||||
self.cancel_button = QtWidgets.QPushButton(
|
||||
strings._("gui_settings_button_cancel")
|
||||
)
|
||||
self.cancel_button.clicked.connect(self.cancel_clicked)
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.addWidget(self.error_label, stretch=1)
|
||||
buttons_layout.addSpacing(20)
|
||||
buttons_layout.addWidget(self.test_tor_button)
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(self.save_button)
|
||||
buttons_layout.addWidget(self.cancel_button)
|
||||
|
||||
# Layout
|
||||
# Main layout
|
||||
main_layout = QtWidgets.QVBoxLayout()
|
||||
main_layout.addWidget(columns_wrapper)
|
||||
main_layout.addStretch()
|
||||
main_layout.addWidget(self.tor_con)
|
||||
main_layout.addStretch()
|
||||
main_layout.addLayout(buttons_layout)
|
||||
self.main_widget = QtWidgets.QWidget()
|
||||
self.main_widget.setLayout(main_layout)
|
||||
|
||||
# Tabs are active label
|
||||
active_tabs_label = QtWidgets.QLabel(
|
||||
strings._("gui_settings_stop_active_tabs_label")
|
||||
)
|
||||
active_tabs_label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||
|
||||
# Active tabs layout
|
||||
active_tabs_layout = QtWidgets.QVBoxLayout()
|
||||
active_tabs_layout.addStretch()
|
||||
active_tabs_layout.addWidget(active_tabs_label)
|
||||
active_tabs_layout.addStretch()
|
||||
self.active_tabs_widget = QtWidgets.QWidget()
|
||||
self.active_tabs_widget.setLayout(active_tabs_layout)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(connection_type_radio_group)
|
||||
layout.addLayout(connection_type_layout)
|
||||
layout.addStretch()
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
layout.addWidget(self.main_widget)
|
||||
layout.addWidget(self.active_tabs_widget)
|
||||
self.setLayout(layout)
|
||||
self.cancel_button.setFocus()
|
||||
|
||||
self.active_tabs_changed(are_tabs_active)
|
||||
self.reload_settings()
|
||||
|
||||
def reload_settings(self):
|
||||
|
@ -392,63 +428,68 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
self.old_settings.get("auth_password")
|
||||
)
|
||||
|
||||
if self.old_settings.get("no_bridges"):
|
||||
self.bridge_use_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.bridge_settings.hide()
|
||||
|
||||
else:
|
||||
if self.old_settings.get("bridges_enabled"):
|
||||
self.bridge_use_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||
self.bridge_settings.show()
|
||||
|
||||
builtin_obfs4 = self.old_settings.get("tor_bridges_use_obfs4")
|
||||
builtin_meek_azure = self.old_settings.get(
|
||||
"tor_bridges_use_meek_lite_azure"
|
||||
)
|
||||
builtin_snowflake = self.old_settings.get("tor_bridges_use_snowflake")
|
||||
|
||||
if builtin_obfs4 or builtin_meek_azure or builtin_snowflake:
|
||||
bridges_type = self.old_settings.get("bridges_type")
|
||||
if bridges_type == "built-in":
|
||||
self.bridge_builtin_radio.setChecked(True)
|
||||
self.bridge_builtin_dropdown.show()
|
||||
if builtin_obfs4:
|
||||
self.bridge_moat_radio.setChecked(False)
|
||||
self.bridge_moat_textbox_options.hide()
|
||||
self.bridge_custom_radio.setChecked(False)
|
||||
self.bridge_custom_textbox_options.hide()
|
||||
|
||||
bridges_builtin_pt = self.old_settings.get("bridges_builtin_pt")
|
||||
if bridges_builtin_pt == "obfs4":
|
||||
self.bridge_builtin_dropdown.setCurrentText("obfs4")
|
||||
elif builtin_meek_azure:
|
||||
elif bridges_builtin_pt == "meek-azure":
|
||||
self.bridge_builtin_dropdown.setCurrentText("meek-azure")
|
||||
elif builtin_snowflake:
|
||||
else:
|
||||
self.bridge_builtin_dropdown.setCurrentText("snowflake")
|
||||
|
||||
self.bridge_moat_textbox_options.hide()
|
||||
self.bridge_custom_textbox_options.hide()
|
||||
|
||||
elif bridges_type == "moat":
|
||||
self.bridge_builtin_radio.setChecked(False)
|
||||
self.bridge_builtin_dropdown.hide()
|
||||
self.bridge_moat_radio.setChecked(True)
|
||||
self.bridge_moat_textbox_options.show()
|
||||
self.bridge_custom_radio.setChecked(False)
|
||||
self.bridge_custom_textbox_options.hide()
|
||||
|
||||
else:
|
||||
self.bridge_builtin_radio.setChecked(False)
|
||||
self.bridge_builtin_dropdown.hide()
|
||||
self.bridge_moat_radio.setChecked(False)
|
||||
self.bridge_moat_textbox_options.hide()
|
||||
self.bridge_custom_radio.setChecked(True)
|
||||
self.bridge_custom_textbox_options.show()
|
||||
|
||||
use_moat = self.old_settings.get("tor_bridges_use_moat")
|
||||
self.bridge_moat_radio.setChecked(use_moat)
|
||||
if use_moat:
|
||||
self.bridge_builtin_dropdown.hide()
|
||||
self.bridge_custom_textbox_options.hide()
|
||||
bridges_moat = self.old_settings.get("bridges_moat")
|
||||
self.bridge_moat_textbox.document().setPlainText(bridges_moat)
|
||||
bridges_custom = self.old_settings.get("bridges_custom")
|
||||
self.bridge_custom_textbox.document().setPlainText(bridges_custom)
|
||||
|
||||
moat_bridges = self.old_settings.get("tor_bridges_use_moat_bridges")
|
||||
self.bridge_moat_textbox.document().setPlainText(moat_bridges)
|
||||
if len(moat_bridges.strip()) > 0:
|
||||
self.bridge_moat_textbox_options.show()
|
||||
else:
|
||||
self.bridge_moat_textbox_options.hide()
|
||||
else:
|
||||
self.bridge_use_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.bridge_settings.hide()
|
||||
|
||||
custom_bridges = self.old_settings.get("tor_bridges_use_custom_bridges")
|
||||
if len(custom_bridges.strip()) != 0:
|
||||
self.bridge_custom_radio.setChecked(True)
|
||||
self.bridge_custom_textbox.setPlainText(custom_bridges)
|
||||
|
||||
self.bridge_builtin_dropdown.hide()
|
||||
self.bridge_moat_textbox_options.hide()
|
||||
self.bridge_custom_textbox_options.show()
|
||||
def active_tabs_changed(self, are_tabs_active):
|
||||
if are_tabs_active:
|
||||
self.main_widget.hide()
|
||||
self.active_tabs_widget.show()
|
||||
else:
|
||||
self.main_widget.show()
|
||||
self.active_tabs_widget.hide()
|
||||
|
||||
def connection_type_bundled_toggled(self, checked):
|
||||
"""
|
||||
Connection type bundled was toggled
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "connection_type_bundled_toggled")
|
||||
self.common.log("TorSettingsTab", "connection_type_bundled_toggled")
|
||||
if checked:
|
||||
self.tor_settings_group.hide()
|
||||
self.connection_type_socks.hide()
|
||||
|
@ -480,7 +521,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
if selection == "meek-azure":
|
||||
# Alert the user about meek's costliness if it looks like they're turning it on
|
||||
if not self.old_settings.get("tor_bridges_use_meek_lite_azure"):
|
||||
if not self.old_settings.get("bridges_builtin_pt") == "meek-azure":
|
||||
Alert(
|
||||
self.common,
|
||||
strings._("gui_settings_meek_lite_expensive_warning"),
|
||||
|
@ -500,7 +541,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Request new bridge button clicked
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "bridge_moat_button_clicked")
|
||||
self.common.log("TorSettingsTab", "bridge_moat_button_clicked")
|
||||
|
||||
moat_dialog = MoatDialog(self.common, self.meek)
|
||||
moat_dialog.got_bridges.connect(self.bridge_moat_got_bridges)
|
||||
|
@ -510,7 +551,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Got new bridges from moat
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "bridge_moat_got_bridges")
|
||||
self.common.log("TorSettingsTab", "bridge_moat_got_bridges")
|
||||
self.bridge_moat_textbox.document().setPlainText(bridges)
|
||||
self.bridge_moat_textbox.show()
|
||||
|
||||
|
@ -527,7 +568,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Connection type automatic was toggled. If checked, hide authentication fields.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "connection_type_automatic_toggled")
|
||||
self.common.log("TorSettingsTab", "connection_type_automatic_toggled")
|
||||
if checked:
|
||||
self.tor_settings_group.hide()
|
||||
self.connection_type_socks.hide()
|
||||
|
@ -538,7 +579,7 @@ class TorSettingsDialog(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.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "connection_type_control_port_toggled")
|
||||
self.common.log("TorSettingsTab", "connection_type_control_port_toggled")
|
||||
if checked:
|
||||
self.tor_settings_group.show()
|
||||
self.connection_type_control_port_extras.show()
|
||||
|
@ -552,7 +593,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
Connection type socket file was toggled. If checked, show extra fields
|
||||
for socket file. If unchecked, hide those extra fields.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "connection_type_socket_file_toggled")
|
||||
self.common.log("TorSettingsTab", "connection_type_socket_file_toggled")
|
||||
if checked:
|
||||
self.tor_settings_group.show()
|
||||
self.connection_type_socket_file_extras.show()
|
||||
|
@ -565,7 +606,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
Authentication option no authentication was toggled.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "authenticate_no_auth_toggled")
|
||||
self.common.log("TorSettingsTab", "authenticate_no_auth_toggled")
|
||||
if checked:
|
||||
self.authenticate_password_extras.hide()
|
||||
else:
|
||||
|
@ -576,39 +617,34 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
Test Tor Settings button clicked. With the given settings, see if we can
|
||||
successfully connect and authenticate to Tor.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "test_tor_clicked")
|
||||
self.common.log("TorSettingsTab", "test_tor_clicked")
|
||||
|
||||
self.error_label.setText("")
|
||||
|
||||
settings = self.settings_from_fields()
|
||||
if not settings:
|
||||
return
|
||||
|
||||
onion = Onion(
|
||||
self.common, use_tmp_dir=True, get_tor_paths=self.common.gui.get_tor_paths
|
||||
self.test_tor_button.hide()
|
||||
self.save_button.hide()
|
||||
|
||||
self.test_onion = Onion(
|
||||
self.common,
|
||||
use_tmp_dir=True,
|
||||
get_tor_paths=self.common.gui.get_tor_paths,
|
||||
)
|
||||
|
||||
tor_con = TorConnectionDialog(self.common, settings, True, onion)
|
||||
tor_con.start()
|
||||
|
||||
# If Tor settings worked, show results
|
||||
if onion.connected_to_tor:
|
||||
Alert(
|
||||
self.common,
|
||||
strings._("settings_test_success").format(
|
||||
onion.tor_version,
|
||||
onion.supports_ephemeral,
|
||||
onion.supports_stealth,
|
||||
onion.supports_v3_onions,
|
||||
),
|
||||
title=strings._("gui_settings_connection_type_test_button"),
|
||||
)
|
||||
|
||||
# Clean up
|
||||
onion.cleanup()
|
||||
self.tor_con_type = "test"
|
||||
self.tor_con.show()
|
||||
self.tor_con.start(settings, True, self.test_onion)
|
||||
|
||||
def save_clicked(self):
|
||||
"""
|
||||
Save button clicked. Save current settings to disk.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "save_clicked")
|
||||
self.common.log("TorSettingsTab", "save_clicked")
|
||||
|
||||
self.error_label.setText("")
|
||||
|
||||
def changed(s1, s2, keys):
|
||||
"""
|
||||
|
@ -631,7 +667,7 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
if not self.common.gui.local_only:
|
||||
if self.common.gui.onion.is_authenticated():
|
||||
self.common.log(
|
||||
"TorSettingsDialog", "save_clicked", "Connected to Tor"
|
||||
"TorSettingsTab", "save_clicked", "Connected to Tor"
|
||||
)
|
||||
|
||||
if changed(
|
||||
|
@ -646,10 +682,11 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
"socket_file_path",
|
||||
"auth_type",
|
||||
"auth_password",
|
||||
"no_bridges",
|
||||
"tor_bridges_use_obfs4",
|
||||
"tor_bridges_use_meek_lite_azure",
|
||||
"tor_bridges_use_custom_bridges",
|
||||
"bridges_enabled",
|
||||
"bridges_type",
|
||||
"bridges_builtin_pt",
|
||||
"bridges_moat",
|
||||
"bridges_custom",
|
||||
],
|
||||
):
|
||||
|
||||
|
@ -657,66 +694,86 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
|
||||
else:
|
||||
self.common.log(
|
||||
"TorSettingsDialog", "save_clicked", "Not connected to Tor"
|
||||
"TorSettingsTab", "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:
|
||||
# Tell the tabs that Tor is disconnected
|
||||
self.tor_is_disconnected.emit()
|
||||
|
||||
# Reinitialize the Onion object
|
||||
self.common.log(
|
||||
"TorSettingsDialog", "save_clicked", "rebooting the Onion"
|
||||
"TorSettingsTab", "save_clicked", "rebooting the Onion"
|
||||
)
|
||||
self.common.gui.onion.cleanup()
|
||||
|
||||
tor_con = TorConnectionDialog(self.common, settings)
|
||||
tor_con.start()
|
||||
|
||||
self.common.log(
|
||||
"TorSettingsDialog",
|
||||
"save_clicked",
|
||||
f"Onion done rebooting, connected to Tor: {self.common.gui.onion.connected_to_tor}",
|
||||
)
|
||||
|
||||
if (
|
||||
self.common.gui.onion.is_authenticated()
|
||||
and not tor_con.wasCanceled()
|
||||
):
|
||||
self.settings_saved.emit()
|
||||
self.close()
|
||||
self.test_tor_button.hide()
|
||||
self.save_button.hide()
|
||||
|
||||
self.tor_con_type = "save"
|
||||
self.tor_con.show()
|
||||
self.tor_con.start(settings)
|
||||
else:
|
||||
self.settings_saved.emit()
|
||||
self.close()
|
||||
self.close_this_tab.emit()
|
||||
else:
|
||||
self.settings_saved.emit()
|
||||
self.close()
|
||||
self.close_this_tab.emit()
|
||||
|
||||
def cancel_clicked(self, openner):
|
||||
def tor_con_success(self):
|
||||
"""
|
||||
Cancel button clicked.
|
||||
Finished testing tor connection.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "cancel_clicked")
|
||||
if (
|
||||
not self.common.gui.local_only
|
||||
and not self.common.gui.onion.is_authenticated()
|
||||
and not (self.openner and self.openner == "autoconnect")
|
||||
):
|
||||
self.tor_con.hide()
|
||||
self.test_tor_button.show()
|
||||
self.save_button.show()
|
||||
|
||||
if self.tor_con_type == "test":
|
||||
Alert(
|
||||
self.common,
|
||||
strings._("gui_tor_connection_canceled"),
|
||||
QtWidgets.QMessageBox.Warning,
|
||||
strings._("settings_test_success").format(
|
||||
self.test_onion.tor_version,
|
||||
self.test_onion.supports_ephemeral,
|
||||
self.test_onion.supports_stealth,
|
||||
self.test_onion.supports_v3_onions,
|
||||
),
|
||||
title=strings._("gui_settings_connection_type_test_button"),
|
||||
)
|
||||
sys.exit()
|
||||
else:
|
||||
self.close()
|
||||
self.test_onion.cleanup()
|
||||
|
||||
elif self.tor_con_type == "save":
|
||||
if (
|
||||
self.common.gui.onion.is_authenticated()
|
||||
and not self.tor_con.wasCanceled()
|
||||
):
|
||||
# Tell the tabs that Tor is connected
|
||||
self.tor_is_connected.emit()
|
||||
# Close the tab
|
||||
self.close_this_tab.emit()
|
||||
|
||||
self.tor_con_type = None
|
||||
|
||||
def tor_con_fail(self, msg):
|
||||
"""
|
||||
Finished testing tor connection.
|
||||
"""
|
||||
self.tor_con.hide()
|
||||
self.test_tor_button.show()
|
||||
self.save_button.show()
|
||||
|
||||
self.error_label.setText(msg)
|
||||
|
||||
if self.tor_con_type == "test":
|
||||
self.test_onion.cleanup()
|
||||
|
||||
self.tor_con_type = None
|
||||
|
||||
def settings_from_fields(self):
|
||||
"""
|
||||
Return a Settings object that's full of values from the settings dialog.
|
||||
"""
|
||||
self.common.log("TorSettingsDialog", "settings_from_fields")
|
||||
self.common.log("TorSettingsTab", "settings_from_fields")
|
||||
settings = Settings(self.common)
|
||||
settings.load() # To get the last update timestamp
|
||||
|
||||
|
@ -753,47 +810,30 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
|
||||
# Whether we use bridges
|
||||
if self.bridge_use_checkbox.checkState() == QtCore.Qt.Checked:
|
||||
settings.set("no_bridges", False)
|
||||
settings.set("bridges_enabled", True)
|
||||
|
||||
if self.bridge_builtin_radio.isChecked():
|
||||
selection = self.bridge_builtin_dropdown.currentText()
|
||||
if selection == "obfs4":
|
||||
settings.set("tor_bridges_use_obfs4", True)
|
||||
settings.set("tor_bridges_use_meek_lite_azure", False)
|
||||
settings.set("tor_bridges_use_snowflake", False)
|
||||
elif selection == "meek-azure":
|
||||
settings.set("tor_bridges_use_obfs4", False)
|
||||
settings.set("tor_bridges_use_meek_lite_azure", True)
|
||||
settings.set("tor_bridges_use_snowflake", False)
|
||||
elif selection == "snowflake":
|
||||
settings.set("tor_bridges_use_obfs4", False)
|
||||
settings.set("tor_bridges_use_meek_lite_azure", False)
|
||||
settings.set("tor_bridges_use_snowflake", True)
|
||||
settings.set("bridges_type", "built-in")
|
||||
|
||||
settings.set("tor_bridges_use_moat", False)
|
||||
settings.set("tor_bridges_use_custom_bridges", "")
|
||||
selection = self.bridge_builtin_dropdown.currentText()
|
||||
settings.set("bridges_builtin_pt", selection)
|
||||
|
||||
if self.bridge_moat_radio.isChecked():
|
||||
settings.set("tor_bridges_use_obfs4", False)
|
||||
settings.set("tor_bridges_use_meek_lite_azure", False)
|
||||
settings.set("tor_bridges_use_snowflake", False)
|
||||
|
||||
settings.set("tor_bridges_use_moat", True)
|
||||
|
||||
settings.set("bridges_type", "moat")
|
||||
moat_bridges = self.bridge_moat_textbox.toPlainText()
|
||||
if moat_bridges.strip() == "":
|
||||
Alert(self.common, strings._("gui_settings_moat_bridges_invalid"))
|
||||
if (
|
||||
self.connection_type_bundled_radio.isChecked()
|
||||
and moat_bridges.strip() == ""
|
||||
):
|
||||
self.error_label.setText(
|
||||
strings._("gui_settings_moat_bridges_invalid")
|
||||
)
|
||||
return False
|
||||
|
||||
settings.set("tor_bridges_use_moat_bridges", moat_bridges)
|
||||
|
||||
settings.set("tor_bridges_use_custom_bridges", "")
|
||||
settings.set("bridges_moat", moat_bridges)
|
||||
|
||||
if self.bridge_custom_radio.isChecked():
|
||||
settings.set("tor_bridges_use_obfs4", False)
|
||||
settings.set("tor_bridges_use_meek_lite_azure", False)
|
||||
settings.set("tor_bridges_use_snowflake", False)
|
||||
settings.set("tor_bridges_use_moat", False)
|
||||
settings.set("bridges_type", "custom")
|
||||
|
||||
new_bridges = []
|
||||
bridges = self.bridge_custom_textbox.toPlainText().split("\n")
|
||||
|
@ -824,27 +864,32 @@ class TorSettingsDialog(QtWidgets.QDialog):
|
|||
|
||||
if bridges_valid:
|
||||
new_bridges = "\n".join(new_bridges) + "\n"
|
||||
settings.set("tor_bridges_use_custom_bridges", new_bridges)
|
||||
settings.set("bridges_custom", new_bridges)
|
||||
else:
|
||||
Alert(self.common, strings._("gui_settings_tor_bridges_invalid"))
|
||||
self.error_label.setText(
|
||||
strings._("gui_settings_tor_bridges_invalid")
|
||||
)
|
||||
return False
|
||||
else:
|
||||
settings.set("no_bridges", True)
|
||||
settings.set("bridges_enabled", False)
|
||||
|
||||
return settings
|
||||
|
||||
def closeEvent(self, e, openner=None):
|
||||
self.common.log("TorSettingsDialog", "closeEvent")
|
||||
def closeEvent(self, e):
|
||||
self.common.log("TorSettingsTab", "closeEvent")
|
||||
|
||||
# On close, if Tor isn't connected, then quit OnionShare altogether
|
||||
if not (self.openner and self.openner == "autoconnect"):
|
||||
if not self.common.gui.local_only:
|
||||
if not self.common.gui.onion.is_authenticated():
|
||||
self.common.log(
|
||||
"TorSettingsDialog",
|
||||
"closeEvent",
|
||||
"Closing while not connected to Tor",
|
||||
)
|
||||
if not self.common.gui.local_only:
|
||||
if not self.common.gui.onion.is_authenticated():
|
||||
self.common.log(
|
||||
"TorSettingsTab",
|
||||
"closeEvent",
|
||||
"Closing while not connected to Tor",
|
||||
)
|
||||
|
||||
# Wait 1ms for the event loop to finish, then quit
|
||||
QtCore.QTimer.singleShot(1, self.common.gui.qtapp.quit)
|
||||
# Wait 1ms for the event loop to finish, then quit
|
||||
QtCore.QTimer.singleShot(1, self.common.gui.qtapp.quit)
|
||||
|
||||
def settings_have_changed(self):
|
||||
# Global settings have changed
|
||||
self.common.log("TorSettingsTab", "settings_have_changed")
|
|
@ -1,309 +0,0 @@
|
|||
---
|
||||
app-id: org.onionshare.OnionShare
|
||||
command: onionshare
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: "5.15"
|
||||
sdk: org.kde.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.golang
|
||||
separate-locales: false
|
||||
finish-args:
|
||||
- "--device=dri"
|
||||
- "--share=ipc"
|
||||
- "--share=network"
|
||||
- "--socket=wayland"
|
||||
- "--socket=x11"
|
||||
- "--talk-name=org.freedesktop.Flatpak"
|
||||
- "--talk-name=org.freedesktop.Notifications"
|
||||
- "--talk-name=org.freedesktop.secrets"
|
||||
- "--filesystem=home:ro"
|
||||
- "--filesystem=~/OnionShare:create"
|
||||
- "--filesystem=xdg-config/onionshare:create"
|
||||
cleanup:
|
||||
- "/go"
|
||||
- "/bin/scripts"
|
||||
modules:
|
||||
- name: pyside2
|
||||
buildsystem: cmake-ninja
|
||||
builddir: true
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_TESTS=OFF
|
||||
cleanup:
|
||||
- /bin
|
||||
sources:
|
||||
- type: archive
|
||||
sha256: f175c1d8813257904cf0efeb58e44f68d53b9916f73adaf9ce19514c0271c3fa
|
||||
url: https://download.qt.io/official_releases/QtForPython/pyside2/PySide2-5.15.1-src/pyside-setup-opensource-src-5.15.1.tar.xz
|
||||
- type: shell
|
||||
commands:
|
||||
- mkdir -p /app/include/qt5tmp && cp -R /usr/include/Qt* /app/include/qt5tmp # https://bugreports.qt.io/browse/PYSIDE-787
|
||||
- sed -i 's|\(--include-paths=\)|\1/app/include/qt5tmp:|' sources/pyside2/cmake/Macros/PySideModules.cmake
|
||||
- name: tor
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- "./configure --prefix=${FLATPAK_DEST}"
|
||||
- make
|
||||
- make install
|
||||
sources:
|
||||
- type: archive
|
||||
sha256: 22cba3794fedd5fa87afc1e512c6ce2c21bc20b4e1c6f8079d832dc1e545e733
|
||||
url: https://dist.torproject.org/tor-0.4.5.6.tar.gz
|
||||
modules:
|
||||
- name: libevent
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- "./configure --prefix=${FLATPAK_DEST}"
|
||||
- make
|
||||
- make install
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
|
||||
sha256: 92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb
|
||||
- name: obfs4proxy
|
||||
buildsystem: simple
|
||||
build-options:
|
||||
env:
|
||||
GOBIN: "/app/bin/"
|
||||
GO111MODULE: "off"
|
||||
build-commands:
|
||||
- ". /usr/lib/sdk/golang/enable.sh; GOPATH=$PWD go install gitlab.com/yawning/obfs4.git/obfs4proxy"
|
||||
sources:
|
||||
- type: git
|
||||
url: https://go.googlesource.com/net
|
||||
commit: 5f55cee0dc0dc168ce29222f077fe7fcd4be72c5
|
||||
dest: src/golang.org/x/net
|
||||
- type: git
|
||||
url: https://go.googlesource.com/crypto
|
||||
commit: 5ea612d1eb830b38bc4e914e37f55311eb58adce
|
||||
dest: src/golang.org/x/crypto
|
||||
- type: git
|
||||
url: https://go.googlesource.com/sys
|
||||
commit: 9a76102bfb4322425a1228caa377974426e82c84
|
||||
dest: src/golang.org/x/sys
|
||||
- type: git
|
||||
url: https://go.googlesource.com/text
|
||||
commit: 8f690f22cf1c026c950adddf3d45258bfd0912f0
|
||||
dest: src/golang.org/x/text
|
||||
- type: git
|
||||
url: https://gitlab.com/yawning/utls
|
||||
commit: 2dd4f38ff9e07464eb2748cc017eac1355e42251
|
||||
dest: src/gitlab.com/yawning/utls.git
|
||||
- type: git
|
||||
url: https://gitlab.com/yawning/obfs4
|
||||
commit: f638c33f6c6f697498150d5f0dfbf26453759262
|
||||
dest: src/gitlab.com/yawning/obfs4.git
|
||||
- type: git
|
||||
url: https://gitlab.com/yawning/bsaes
|
||||
commit: 0a714cd429ec754482b4001e918db30cd2094405
|
||||
dest: src/gitlab.com/yawning/bsaes.git
|
||||
- type: git
|
||||
url: https://github.com/dchest/siphash
|
||||
commit: a21c2e7914a8fe0db087fa007cbe804967665dfc
|
||||
dest: src/github.com/dchest/siphash
|
||||
- type: git
|
||||
url: https://github.com/dsnet/compress
|
||||
commit: da652975a8eea9fa0735aba8056747a751db0bd3
|
||||
dest: src/github.com/dsnet/compress
|
||||
- type: git
|
||||
url: https://git.torproject.org/pluggable-transports/goptlib
|
||||
commit: 781a46c66d2ddbc3509354ae7f1fccab74cb9927
|
||||
dest: src/git.torproject.org/pluggable-transports/goptlib.git
|
||||
- name: onionshare
|
||||
buildsystem: simple
|
||||
ensure-writable:
|
||||
- easy-install.pth
|
||||
build-commands:
|
||||
- python3 setup.py install --prefix=${FLATPAK_DEST}
|
||||
- install -D -m0644 org.onionshare.OnionShare.appdata.xml ${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.appdata.xml
|
||||
- install -D -m0644 org.onionshare.OnionShare.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/org.onionshare.OnionShare.svg
|
||||
- install -D -m0644 org.onionshare.OnionShare.desktop ${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../desktop/src
|
||||
modules:
|
||||
- name: python3-qrcode
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"qrcode"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/19/d5/6c7d4e103d94364d067636417a77a6024219c58cd6e9f428ece9b5061ef9/qrcode-6.1.tar.gz
|
||||
sha256: 505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369
|
||||
- name: onionshare-cli
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- python3 setup.py install --prefix=${FLATPAK_DEST}
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../cli
|
||||
modules:
|
||||
- name: python3-click
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"click"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz
|
||||
sha256: d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a
|
||||
- name: python3-flask
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"flask"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz
|
||||
sha256: a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz
|
||||
sha256: d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz
|
||||
sha256: 29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz
|
||||
sha256: 321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/4e/0b/cb02268c90e67545a0e3a37ea1ca3d45de3aca43ceb7dbf1712fb5127d5d/Flask-1.1.2.tar.gz
|
||||
sha256: 4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz
|
||||
sha256: 6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c
|
||||
- name: python3-flask-socketio
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"flask-socketio"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/06/7f/7496b6684e2b8eadb150555fa979497303459a31cb7dc592a5da51900090/Flask-SocketIO-5.0.1.tar.gz
|
||||
sha256: 5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz
|
||||
sha256: a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/27/6f/be940c8b1f1d69daceeb0032fee6c34d7bd70e3e649ccac0951500b4720e/click-7.1.2.tar.gz
|
||||
sha256: d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz
|
||||
sha256: 29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/68/1a/f27de07a8a304ad5fa817bbe383d1238ac4396da447fa11ed937039fa04b/itsdangerous-1.1.0.tar.gz
|
||||
sha256: 321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/4e/0b/cb02268c90e67545a0e3a37ea1ca3d45de3aca43ceb7dbf1712fb5127d5d/Flask-1.1.2.tar.gz
|
||||
sha256: 4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/bd/7c/83fbbc8568be511bc48704b97ef58f67ff2ab85ec4fcd1dad12cd2323c32/bidict-0.21.2.tar.gz
|
||||
sha256: 4fa46f7ff96dc244abfc437383d987404ae861df797e2fd5b190e233c302be09
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/10/27/a33329150147594eff0ea4c33c2036c0eadd933141055be0ff911f7f8d04/Werkzeug-1.0.1.tar.gz
|
||||
sha256: 6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/37/91/7713854e0741f807c38ef084169b489ff6e87b24d1d9ba1e943bb9e10b8b/python-socketio-5.0.4.tar.gz
|
||||
sha256: f53fd0d5bd9f75a70492062f4ae6195ab5d34d67a29024d740f25e468392893e
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/92/e8/2dd4bd782b593adcc0bdce0675fe92016c3ffca061202142fcf1e55cbf6a/python-engineio-4.0.0.tar.gz
|
||||
sha256: 9f34afa4170f5ba6e3d9ff158752ccf8fbb2145f16554b2f0fc84646675be99a
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/12/68/95515eaff788370246dac534830ea9ccb0758e921ac9e9041996026ecaf2/setuptools-53.0.0.tar.gz
|
||||
sha256: 1b18ef17d74ba97ac9c0e4b4265f123f07a8ae85d9cd093949fa056d3eeeead5
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/ed/46/e298a50dde405e1c202e316fa6a3015ff9288423661d7ea5e8f22f589071/wheel-0.36.2.tar.gz
|
||||
sha256: e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/af/df/f8aa8a78d4d29e0cffa4512e9bc223ed02f24893fe1837c6cee2749ebd67/setuptools_scm-5.0.1.tar.gz
|
||||
sha256: c85b6b46d0edd40d2301038cdea96bb6adc14d62ef943e75afb08b3e7bcf142a
|
||||
- name: python3-psutil
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"psutil"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/e1/b0/7276de53321c12981717490516b7e612364f2cb372ee8901bd4a66a000d7/psutil-5.8.0.tar.gz
|
||||
sha256: 0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6
|
||||
- name: python3-pycryptodome
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"pycryptodome"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/88/7f/740b99ffb8173ba9d20eb890cc05187677df90219649645aca7e44eb8ff4/pycryptodome-3.10.1.tar.gz
|
||||
sha256: 3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673
|
||||
- name: python3-pysocks
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"pysocks"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz
|
||||
sha256: 3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||
- name: python3-requests
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"requests"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/d7/8d/7ee68c6b48e1ec8d41198f694ecdc15f7596356f2ff8e6b1420300cf5db3/urllib3-1.26.3.tar.gz
|
||||
sha256: de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/ee/2d/9cdc2b527e127b4c9db64b86647d567985940ac3698eeabc7ffaccb4ea61/chardet-4.0.0.tar.gz
|
||||
sha256: 0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/06/a9/cd1fd8ee13f73a4d4f491ee219deeeae20afefa914dfb4c130cfc9dc397a/certifi-2020.12.5.tar.gz
|
||||
sha256: 1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/6b/47/c14abc08432ab22dc18b9892252efaf005ab44066de871e72a38d6af464b/requests-2.25.1.tar.gz
|
||||
sha256: 27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/ea/b7/e0e3c1c467636186c39925827be42f16fee389dc404ac29e930e9136be70/idna-2.10.tar.gz
|
||||
sha256: b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6
|
||||
- name: python3-stem
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"stem"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/71/bd/ab05ffcbfe74dca704e860312e00c53ef690b1ddcb23be7a4d9ea4f40260/stem-1.8.0.tar.gz
|
||||
sha256: a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2
|
||||
- name: python3-unidecode
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"unidecode"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/cd/31/245d8a384939aa0ee152c76fc62890f79f35fc41cd12839f5df268d9081d/Unidecode-1.2.0.tar.gz
|
||||
sha256: 8d73a97d387a956922344f6b74243c2c6771594659778744b2dbdaad8f6b727d
|
||||
- name: python3-urllib3
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"urllib3"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/d7/8d/7ee68c6b48e1ec8d41198f694ecdc15f7596356f2ff8e6b1420300cf5db3/urllib3-1.26.3.tar.gz
|
||||
sha256: de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73
|
||||
- name: python3-eventlet
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST}
|
||||
"eventlet"
|
||||
sources:
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/40/9c/bd7bc0202a84012a4b6b653b54a389ef48bc7f13ce628865357ffdf37160/eventlet-0.30.1.tar.gz
|
||||
sha256: d00649a7e17de0bcddff1a96311ed3baf1b295b3223d4b71aceafe7b45e6d6f8
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/ec/c5/14bcd63cb6d06092a004793399ec395405edf97c2301dfdc146dfbd5beed/dnspython-1.16.0.zip
|
||||
sha256: 36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01
|
||||
- type: file
|
||||
url: https://files.pythonhosted.org/packages/92/be/878cc5314fa5aadce33e68738c1a24debe317605196bdfc2049e66bc9c30/greenlet-1.0.0.tar.gz
|
||||
sha256: 719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8
|
|
@ -1,6 +1,6 @@
|
|||
name: onionshare
|
||||
base: core18
|
||||
version: '2.4'
|
||||
version: '2.4.1'
|
||||
summary: Securely and anonymously share files, host websites, and chat using Tor
|
||||
description: |
|
||||
OnionShare lets you securely and anonymously send and receive files. It works by starting
|
||||
|
@ -15,6 +15,7 @@ apps:
|
|||
onionshare:
|
||||
common-id: org.onionshare.OnionShare
|
||||
command: onionshare
|
||||
extensions: [ gnome-3-34 ]
|
||||
plugs:
|
||||
- desktop
|
||||
- home
|
||||
|
@ -125,7 +126,7 @@ parts:
|
|||
- setuptools
|
||||
- pynacl
|
||||
- colorama
|
||||
- git+https://github.com/onionshare/stem.git@1.8.1
|
||||
- cepa == 1.8.3
|
||||
stage-packages:
|
||||
- build-essential
|
||||
- libssl-dev
|
||||
|
@ -135,11 +136,11 @@ parts:
|
|||
stage:
|
||||
- -usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
|
||||
- -usr/share/doc/libssl1.1/changelog.Debian.gz
|
||||
after: [tor, obfs4]
|
||||
after: [tor, obfs4, snowflake-client, meek-client]
|
||||
|
||||
tor:
|
||||
source: https://dist.torproject.org/tor-0.4.6.7.tar.gz
|
||||
source-checksum: sha256/ff665ce121b2952110bd98b9c8741b5593bf6c01ac09033ad848ed92c2510f9a
|
||||
source: https://dist.torproject.org/tor-0.4.6.8.tar.gz
|
||||
source-checksum: sha256/15ce1a37b4cc175b07761e00acdcfa2c08f0d23d6c3ab9c97c464bd38cc5476a
|
||||
source-type: tar
|
||||
plugin: autotools
|
||||
build-packages:
|
||||
|
@ -158,3 +159,24 @@ parts:
|
|||
go-importpath: gitlab.com/yawning/obfs4
|
||||
source: https://gitlab.com/yawning/obfs4
|
||||
source-type: git
|
||||
|
||||
snowflake-client:
|
||||
plugin: go
|
||||
go-importpath: git.torproject.org/pluggable-transports/snowflake.git/client
|
||||
source: https://git.torproject.org/pluggable-transports/snowflake.git
|
||||
source-type: git
|
||||
source-tag: v2.0.1
|
||||
organize:
|
||||
bin/client: bin/snowflake-client
|
||||
|
||||
meek-client:
|
||||
plugin: go
|
||||
go-channel: stable
|
||||
go-importpath: git.torproject.org/pluggable-transports/meek.git/meek-client
|
||||
# Not sure why I have to do this, but it works
|
||||
override-build: |
|
||||
cd meek-client
|
||||
go build -o /root/parts/meek-client/install/bin ./...
|
||||
source: https://git.torproject.org/pluggable-transports/meek.git
|
||||
source-type: git
|
||||
source-tag: v0.37.0
|
||||
|
|
Loading…
Reference in a new issue