Merge branch 'censorship' of github.com:onionshare/onionshare into auto-connect-ui

This commit is contained in:
Saptak S 2021-11-19 18:29:47 +05:30
commit 3422364fa1
No known key found for this signature in database
GPG key ID: 7B7F1772C0C6FCBF
35 changed files with 1205 additions and 1104 deletions

View file

@ -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:

View file

@ -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`.

View file

@ -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)

View file

@ -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):
"""

View file

@ -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):

View file

@ -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

View file

@ -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;

View file

@ -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>&mdash;</div>
<div class="cell-data">&mdash;</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>

View file

@ -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
View file

@ -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"},

View file

@ -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 = "*"

View file

@ -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"
)

View file

@ -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`:

View file

@ -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__":

View file

@ -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

View file

@ -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"])

View file

@ -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(

View file

@ -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):

View file

@ -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")

View file

@ -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

View file

@ -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"
}

View file

@ -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()

View file

@ -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]

View file

@ -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()

View file

@ -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):
"""

View file

@ -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):
"""

View file

@ -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):
"""

View file

@ -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()

View file

@ -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()

View file

@ -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)

View file

@ -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):
"""

View file

@ -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()

View file

@ -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")

View file

@ -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

View file

@ -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