Merge branch 'develop' of github.com:onionshare/onionshare into ros-fixes

This commit is contained in:
Micah Lee 2022-01-16 15:49:33 -08:00
commit 9e99ad8b8d
No known key found for this signature in database
GPG Key ID: 403C2657CD994F73
328 changed files with 8237 additions and 3165 deletions

View File

@ -9,7 +9,7 @@ workflows:
jobs:
test-cli:
docker:
- image: circleci/python:3.8-buster
- image: circleci/python:3.9-bullseye
working_directory: ~/repo
@ -37,7 +37,7 @@ jobs:
test-gui:
docker:
- image: circleci/python:3.8-buster
- image: circleci/python:3.9-bullseye
working_directory: ~/repo
@ -50,16 +50,11 @@ jobs:
sudo apt-get update
sudo apt-get install -y tor obfs4proxy gcc python3-dev python3-pyside2.qtcore python3-pyside2.qtwidgets python3-pyside2.qtgui
sudo apt-get install -y xvfb x11-utils libxkbcommon-x11-0 libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-render-util0 libxcb-icccm4 libxcb-keysyms1 libxcb-image0
cd ~/repo/cli
poetry install
poetry build
cp dist/onionshare_cli-*.whl ~/repo/desktop
cd ~/repo/desktop
pip install briefcase pytest pytest-briefcase pytest-faulthandler pytest-qt pytest-xvfb
pip install $(python -c 'import toml; print(" ".join(toml.loads(open("pyproject.toml").read())["tool"]["briefcase"]["app"]["onionshare"]["requires"]))')
poetry install
- run:
name: Run tests
command: |
cd ~/repo/desktop
./tests/run.sh
xvfb-run poetry run ./tests/run.sh

8
.gitignore vendored
View File

@ -59,10 +59,4 @@ venv
# other
.vscode
onionshare.dist-info
desktop/src/onionshare/resources/tor
desktop/*.whl
desktop/linux
desktop/windows
desktop/macOS
# gets added automatically when building for Windows or macOS
desktop/include
desktop/onionshare/resources/tor

View File

@ -110,7 +110,7 @@
## 1.3
* Major UI redesign, introducing many UX improvements
* Client-side web interfact redesigned
* Client-side web interface redesigned
* New feature: Support for meek_lite pluggable transports (Amazon and Azure) - not yet ready for Windows or macOS, sorry
* New feature: Support for custom obfs4 and meek_lite bridges (again, meek_lite not available on Windows/macOS yet)
* New feature: Ability to cancel share before it starts

View File

@ -6,7 +6,7 @@ Copyright (C) 2014-2021 Micah Lee, et al. <micah@micahflee.com>
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -650,7 +650,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@ -669,11 +669,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@ -9,9 +9,9 @@ Before making a release, you must update the version in these places:
- [ ] `cli/pyproject.toml`
- [ ] `cli/setup.py`
- [ ] `cli/onionshare_cli/resources/version.txt`
- [ ] `desktop/pyproject.toml` (under `version` and **don't forget** the `./onionshare_cli-$VERSION-py3-none-any.whl` dependency)
- [ ] `desktop/src/setup.py`
- [ ] `desktop/src/org.onionshare.OnionShare.appdata.xml`
- [ ] `desktop/pyproject.toml`
- [ ] `desktop/setup.py`
- [ ] `desktop/org.onionshare.OnionShare.appdata.xml`
- [ ] `docs/source/conf.py` (`version` at the top, and the `versions` list too)
- [ ] `snap/snapcraft.yaml`
@ -19,6 +19,7 @@ If you update `flask-socketio`, ensure that you also update the [socket.io.min.j
Use tor binaries from the latest Tor Browser:
- [ ] `desktop/scripts/get-tor-linux.py`
- [ ] `desktop/scripts/get-tor-osx.py`
- [ ] `desktop/scripts/get-tor-windows.py`
@ -42,7 +43,7 @@ Finalize localization:
You also must edit these files:
- [ ] `desktop/src/org.onionshare.OnionShare.appdata.xml` should have the correct release date, and links to correct screenshots
- [ ] `desktop/org.onionshare.OnionShare.appdata.xml` should have the correct release date, and links to correct screenshots
- [ ] `CHANGELOG.md` should be updated to include a list of all major changes since the last release
Make sure snapcraft packaging works. In `snap/snapcraft.yaml`:
@ -97,73 +98,45 @@ snapcraft login
snapcraft upload --release=stable onionshare_$VERSION_amd64.snap
```
## Linux AppImage release
_Note: AppImage packages are currently broken due to [this briefcase bug](https://github.com/beeware/briefcase/issues/504). Until it's fixed, OnionShare for Linux will only be available in Flatpak and Snapcraft._
Set up the development environment described in `README.md`.
Make sure your virtual environment is active:
```sh
. venv/bin/activate
```
Run the AppImage build script:
```sh
./package/linux/build-appimage.py
```
## Windows
Set up the development environment described in `README.md`. And install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) and add `C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86` to your path.
Set up the development environment described in desktop `README.md`.
Make sure your virtual environment is active:
```
venv\Scripts\activate.bat
```
- To get `signtool.exe`, install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) and add `C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x86` to your path.
- Go to https://dotnet.microsoft.com/download/dotnet-framework and download and install .NET Framework 3.5 SP1 Runtime. I downloaded `dotnetfx35.exe`.
- Go to https://wixtoolset.org/releases/ and download and install WiX toolset. I downloaded `wix311.exe`. Add `C:\Program Files (x86)\WiX Toolset v3.11\bin` to the path.
Run the Windows build script:
```
python package\windows\build.py
poetry run python .\package\build-windows.py
```
This will create `desktop/windows/OnionShare-$VERSION.msi`, signed.
This will create `desktop/dist/OnionShare-$VERSION.msi`, signed.
## macOS
Set up the development environment described in `README.md`. And install `create-dmg`:
Set up the development environment described in `README.md`.
Then build an executable, make it a macOS app bundle, and package it in a dmg:
```sh
brew install create-dmg
poetry run ./package/build-mac.py
```
Make sure your virtual environment is active:
```sh
. venv/bin/activate
```
Run the macOS build script:
```sh
./package/macos/build.py --with-codesign
```
The will create `dist/OnionShare-$VERSION.dmg`.
Now, notarize the release. You must have an app-specific Apple ID password saved in the login keychain called `onionshare-notarize`.
- Notarize it: `xcrun altool --notarize-app --primary-bundle-id "com.micahflee.onionshare" -u "micah@micahflee.com" -p "@keychain:onionshare-notarize" --file macOS/OnionShare.dmg`
- Notarize it: `xcrun altool --notarize-app --primary-bundle-id "com.micahflee.onionshare" -u "micah@micahflee.com" -p "@keychain:onionshare-notarize" --file dist/OnionShare-$VERSION.dmg`
- Wait for it to get approved, check status with: `xcrun altool --notarization-history 0 -u "micah@micahflee.com" -p "@keychain:onionshare-notarize"`
- After it's approved, staple the ticket: `xcrun stapler staple macOS/OnionShare.dmg`
- After it's approved, staple the ticket: `xcrun stapler staple dist/OnionShare-$VERSION.dmg`
This will create `desktop/macOS/OnionShare.dmg`, signed and notarized.
This will create `desktop/dist/OnionShare-$VERSION.dmg`, signed and notarized.
## Source package
To make a source package, run `./build-source.sh $TAG`, where `$TAG` is the the name of the signed git tag, e.g. `v2.1`.
To make a source package, run `./build-source.sh $TAG`, where `$TAG` is the name of the signed git tag, e.g. `v2.1`.
This will create `dist/onionshare-$VERSION.tar.gz`.

View File

@ -59,28 +59,13 @@ pip install --user onionshare-cli
#### Set path
When you install programs with pip and use the --user flag, it installs them into ~/.local/bin, which isn't in your path by default. To add ~/.local/bin to your path automatically for the next time you reopen the terminal or source your shell configuration file, do the following:
When you install programs with pip and use the `--user` flag, it installs them into *~/.local/bin*, which isn't in your path by default. To add *~/.local/bin* to your path automatically for the next time you reopen the terminal or source your shell configuration file, do the following:
First, discover what shell you are using:
Apply the path to your shell file:
```sh
echo $SHELL
```
Then apply the path to your shell file:
bash:
```sh
echo "PATH=\$PATH:~/.local/bin" >> ~/.bashrc
source ~/.bashrc
```
zsh:
```sh
echo "PATH=\$PATH:~/.local/bin" >> ~/.zshrc
source ~/.zshrc
printf "PATH=\$PATH:~/.local/bin\n" >> ~/.${SHELL##*/}rc
. ~/.${SHELL##*/}rc
```
#### Usage
@ -112,11 +97,3 @@ To run tests:
```sh
poetry run pytest -v ./tests
```
## Build a wheel package
```sh
poetry build
```
This will create `dist/onionshare_cli-$VERSION-py3-none-any.whl`.

View File

@ -25,21 +25,46 @@ from .meek import MeekNotRunning
class CensorshipCircumvention(object):
"""
Connect to the Tor Moat APIs to retrieve censorship
circumvention recommendations, over the Meek client.
circumvention recommendations or the latest bridges.
We support reaching this API over Tor, or Meek
(domain fronting) if Tor is not connected.
"""
def __init__(self, common, meek, domain_fronting=True):
def __init__(self, common, meek=None, onion=None):
"""
Set up the CensorshipCircumvention object to hold
common and meek objects.
"""
self.common = common
self.meek = meek
self.common.log("CensorshipCircumvention", "__init__")
# Bail out if we requested domain fronting but we can't use meek
if domain_fronting and not self.meek.meek_proxies:
self.api_proxies = {}
if meek:
self.meek = meek
if not self.meek.meek_proxies:
raise MeekNotRunning()
else:
self.common.log(
"CensorshipCircumvention",
"__init__",
"Using Meek with CensorShipCircumvention API",
)
self.api_proxies = self.meek.meek_proxies
if onion:
self.onion = onion
if not self.onion.is_authenticated:
return False
else:
self.common.log(
"CensorshipCircumvention",
"__init__",
"Using Tor with CensorShipCircumvention API",
)
(socks_address, socks_port) = self.onion.get_tor_socks_port()
self.api_proxies = {
"http": f"socks5h://{socks_address}:{socks_port}",
"https": f"socks5h://{socks_address}:{socks_port}",
}
def request_map(self, country=False):
"""
@ -52,6 +77,8 @@ class CensorshipCircumvention(object):
Note that this API endpoint doesn't return actual bridges,
it just returns the recommended bridge type countries.
"""
if not self.api_proxies:
return False
endpoint = "https://bridges.torproject.org/moat/circumvention/map"
data = {}
if country:
@ -61,7 +88,7 @@ class CensorshipCircumvention(object):
endpoint,
json=data,
headers={"Content-Type": "application/vnd.api+json"},
proxies=self.meek.meek_proxies,
proxies=self.api_proxies,
)
if r.status_code != 200:
self.common.log(
@ -95,6 +122,8 @@ class CensorshipCircumvention(object):
Optionally, a list of transports can be specified in order to
return recommended settings for just that transport type.
"""
if not self.api_proxies:
return False
endpoint = "https://bridges.torproject.org/moat/circumvention/settings"
data = {}
if country:
@ -105,7 +134,7 @@ class CensorshipCircumvention(object):
endpoint,
json=data,
headers={"Content-Type": "application/vnd.api+json"},
proxies=self.meek.meek_proxies,
proxies=self.api_proxies,
)
if r.status_code != 200:
self.common.log(
@ -142,11 +171,13 @@ class CensorshipCircumvention(object):
"""
Retrieves the list of built-in bridges from the Tor Project.
"""
if not self.api_proxies:
return False
endpoint = "https://bridges.torproject.org/moat/circumvention/builtin"
r = requests.post(
endpoint,
headers={"Content-Type": "application/vnd.api+json"},
proxies=self.meek.meek_proxies,
proxies=self.api_proxies,
)
if r.status_code != 200:
self.common.log(

View File

@ -329,23 +329,49 @@ class Common:
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":
# In Windows, the Tor binaries are in the onionshare package, not the onionshare_cli package
base_path = self.get_resource_path("tor")
base_path = base_path.replace("onionshare_cli", "onionshare")
tor_path = os.path.join(base_path, "Tor", "tor.exe")
# If tor.exe isn't there, mayber we're running from the source tree
if not os.path.exists(tor_path):
base_path = os.path.join(os.getcwd(), "onionshare", "resources", "tor")
tor_path = os.path.join(base_path, "Tor", "tor.exe")
if not os.path.exists(tor_path):
raise CannotFindTor()
obfs4proxy_file_path = os.path.join(base_path, "Tor", "obfs4proxy.exe")
snowflake_file_path = os.path.join(base_path, "Tor", "snowflake-client.exe")
meek_client_file_path = os.path.join(base_path, "Tor", "meek-client.exe")
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":
# Let's see if we have tor binaries in the onionshare GUI package
base_path = self.get_resource_path("tor")
base_path = base_path.replace("onionshare_cli", "onionshare")
tor_path = os.path.join(base_path, "tor")
if os.path.exists(tor_path):
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")
tor_geo_ip_file_path = os.path.join(base_path, "geoip")
tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
else:
# Fallback to looking in the path
tor_path = shutil.which("tor")
if not tor_path:
if not os.path.exists(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"

View File

@ -20,8 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import subprocess
import time
from queue import Queue, Empty
from threading import Thread
class Meek(object):
@ -67,14 +65,6 @@ class Meek(object):
Start the Meek Client and populate the SOCKS proxies dict
for use with requests to the Tor Moat API.
"""
# Small method to read stdout from the subprocess.
# We use this to obtain the random port that Meek
# started on
def enqueue_output(out, queue):
for line in iter(out.readline, b""):
queue.put(line)
out.close()
# Abort early if we can't find the Meek client
if self.meek_client_file_path is None or not os.path.exists(
self.meek_client_file_path
@ -124,21 +114,8 @@ class Meek(object):
universal_newlines=True,
)
# Queue up the stdout from the subprocess for polling later
q = Queue()
t = Thread(target=enqueue_output, args=(self.meek_proc.stdout, q))
t.daemon = True # thread dies with the program
t.start()
while True:
# read stdout without blocking
try:
line = q.get_nowait()
self.common.log("Meek", "start", line.strip())
except Empty:
# no stdout yet?
pass
else: # we got stdout
# Obtain the host and port that meek is running on
for line in iter(self.meek_proc.stdout.readline, b""):
if "CMETHOD meek socks5" in line:
self.meek_host = line.split(" ")[3].split(":")[0]
self.meek_port = line.split(" ")[3].split(":")[1]
@ -152,6 +129,7 @@ class Meek(object):
if "CMETHOD-ERROR" in line:
self.cleanup()
raise MeekNotRunning()
break
if self.meek_port:
self.meek_proxies = {

View File

@ -18,17 +18,19 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from .censorship import CensorshipCircumvention
from .meek import Meek
from stem.control import Controller
from stem import ProtocolError, SocketClosed
from stem.connection import MissingPassword, UnreadableCookieFile, AuthenticationFailure
import base64
import nacl.public
import os
import tempfile
import subprocess
import time
import shlex
import psutil
import shlex
import subprocess
import tempfile
import time
import traceback
from distutils.version import LooseVersion as Version
@ -258,9 +260,7 @@ class Onion(object):
and cmdline[2] == self.tor_torrc
):
self.common.log(
"Onion",
"connect",
"found a stale tor process, killing it",
"Onion", "connect", "found a stale tor process, killing it"
)
proc.terminate()
proc.wait()
@ -317,49 +317,75 @@ class Onion(object):
)
with open(self.tor_torrc, "w") as f:
self.common.log("Onion", "connect", "Writing torrc template file")
f.write(torrc_template)
# Bridge support
if self.settings.get("bridges_enabled"):
f.write("\nUseBridges 1\n")
if self.settings.get("bridges_type") == "built-in":
if self.settings.get("bridges_builtin_pt") == "obfs4":
use_torrc_bridge_templates = False
builtin_bridge_type = self.settings.get("bridges_builtin_pt")
# Use built-inbridges stored in settings, if they are there already.
# They are probably newer than that of our hardcoded copies.
if self.settings.get("bridges_builtin"):
try:
for line in self.settings.get("bridges_builtin")[
builtin_bridge_type
]:
if line.strip() != "":
f.write(f"Bridge {line}\n")
self.common.log(
"Onion",
"connect",
"Wrote in the built-in bridges from OnionShare settings",
)
except KeyError:
# Somehow we had built-in bridges in our settings, but
# not for this bridge type. Fall back to using the hard-
# coded templates.
use_torrc_bridge_templates = True
else:
use_torrc_bridge_templates = True
if use_torrc_bridge_templates:
if builtin_bridge_type == "obfs4":
with open(
self.common.get_resource_path("torrc_template-obfs4")
self.common.get_resource_path(
"torrc_template-obfs4"
)
) as o:
f.write(o.read())
elif self.settings.get("bridges_builtin_pt") == "meek-azure":
elif builtin_bridge_type == "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":
elif builtin_bridge_type == "snowflake":
with open(
self.common.get_resource_path(
"torrc_template-snowflake"
)
) as o:
f.write(o.read())
self.common.log(
"Onion",
"connect",
"Wrote in the built-in bridges from torrc templates",
)
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("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",
)
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
@ -385,19 +411,15 @@ class Onion(object):
)
# Wait for the tor controller to start
self.common.log(
"Onion",
"connect",
f"tor pid: {self.tor_proc.pid}",
)
self.common.log("Onion", "connect", f"tor pid: {self.tor_proc.pid}")
time.sleep(2)
return_code = self.tor_proc.poll()
if return_code != None:
self.common.log("Onion", "connect", f"tor process has terminated early: {return_code}")
# Connect to the controller
self.common.log(
"Onion",
"connect",
"authenticating to tor controller",
)
self.common.log("Onion", "connect", "authenticating to tor controller")
try:
if (
self.common.platform == "Windows"
@ -638,6 +660,14 @@ class Onion(object):
# https://trac.torproject.org/projects/tor/ticket/28619
self.supports_v3_onions = self.tor_version >= Version("0.3.5.7")
# Now that we are connected to Tor, if we are using built-in bridges,
# update them with the latest copy available from the Tor API
if (
self.settings.get("bridges_enabled")
and self.settings.get("bridges_type") == "built-in"
):
self.update_builtin_bridges()
def is_authenticated(self):
"""
Returns True if the Tor connection is still working, or False otherwise.
@ -881,3 +911,68 @@ class Onion(object):
return ("127.0.0.1", 9150)
else:
return (self.settings.get("socks_address"), self.settings.get("socks_port"))
def update_builtin_bridges(self):
"""
Use the CensorshipCircumvention API to fetch the latest built-in bridges
and update them in settings.
"""
builtin_bridges = False
meek = None
# Try obtaining bridges over Tor, if we're connected to it.
if self.is_authenticated:
self.common.log(
"Onion",
"update_builtin_bridges",
"Updating the built-in bridges. Trying over Tor first",
)
self.censorship_circumvention = CensorshipCircumvention(
self.common, None, self
)
builtin_bridges = self.censorship_circumvention.request_builtin_bridges()
if not builtin_bridges:
# Tor was not running or it failed to hit the Tor API.
# Fall back to using Meek (domain-fronting).
self.common.log(
"Onion",
"update_builtin_bridges",
"Updating the built-in bridges. Trying via Meek (no Tor)",
)
meek = Meek(self.common)
meek.start()
self.censorship_circumvention = CensorshipCircumvention(
self.common, meek, None
)
builtin_bridges = self.censorship_circumvention.request_builtin_bridges()
meek.cleanup()
if builtin_bridges:
# If we got to this point, we have bridges
self.common.log(
"Onion",
"update_builtin_bridges",
f"Obtained bridges: {builtin_bridges}",
)
if builtin_bridges["meek"]:
# Meek bridge needs to be defined as "meek_lite", not "meek",
# for it to work with obfs4proxy.
# We also refer to this bridge type as 'meek-azure' in our settings.
# So first, rename the key in the dict
builtin_bridges["meek-azure"] = builtin_bridges.pop("meek")
new_meek_bridges = []
# Now replace the values. They also need the url/front params appended
for item in builtin_bridges["meek-azure"]:
newline = item.replace("meek", "meek_lite")
new_meek_bridges.append(
f"{newline} url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com"
)
builtin_bridges["meek-azure"] = new_meek_bridges
# Save the new settings
self.settings.set("bridges_builtin", builtin_bridges)
self.settings.save()
else:
self.common.log(
"Onion", "update_builtin_bridges", "Error getting built-in bridges"
)
return False

View File

@ -40,9 +40,6 @@ class OnionShare(object):
self.onion_host = None
self.port = None
# files and dirs to delete on shutdown
self.cleanup_filenames = []
# do not use tor -- for development
self.local_only = local_only
@ -75,7 +72,9 @@ class OnionShare(object):
if self.local_only:
self.onion_host = f"127.0.0.1:{self.port}"
if not mode_settings.get("general", "public"):
self.auth_string = "E2GOT5LTUTP3OAMRCRXO4GSH6VKJEUOXZQUC336SRKAHTTT5OVSA"
self.auth_string = (
"E2GOT5LTUTP3OAMRCRXO4GSH6VKJEUOXZQUC336SRKAHTTT5OVSA"
)
return
self.onion_host = self.onion.start_onion_service(

View File

@ -154,7 +154,7 @@ var getScrollDiffBefore = function () {
var scrollBottomMaybe = function (scrollDiff) {
// Scrolls to bottom if the user is scrolled at bottom
// if the user has scrolled upp, it wont scroll at bottom.
// if the user has scrolled up, it won't scroll at bottom.
// Note: when a user themselves send a message, it will still
// scroll to the bottom even if they had scrolled up before.
if (scrollDiff > 0) {

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

@ -1,3 +1,2 @@
# Enable built-in meek-azure bridge
Bridge meek_lite 0.0.2.0:3 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
UseBridges 1
# Enable built-in meek bridge
Bridge meek_lite 0.0.2.0:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com

View File

@ -1,17 +1,16 @@
# Enable built-in obfs4-bridge
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
# Enable built-in obfs4 bridge
Bridge obfs4 144.217.20.138:80 FB70B257C162BF1038CA669D568D76F5B7F0BABB cert=vYIV5MgrghGQvZPIi1tJwnzorMgqgmlKaB77Y3Z9Q/v94wZBOAXkW+fdx4aSxLVnKO+xNw iat-mode=0
Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
Bridge obfs4 [2a0c:4d80:42:702::1]:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
UseBridges 1
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0
Bridge obfs4 [2a0c:4d80:42:702::1]:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0

View File

@ -1,3 +1,2 @@
# Enable built-in snowflake bridge
Bridge snowflake 192.0.2.3:1 2B280B23E1107BB62ABFC40DDCC8824814F80A72
UseBridges 1
Bridge snowflake 0.0.3.0:1 2B280B23E1107BB62ABFC40DDCC8824814F80A72

View File

@ -110,6 +110,7 @@ class Settings(object):
"bridges_builtin_pt": "obfs4", # "obfs4", "meek-azure", or "snowflake"
"bridges_moat": "",
"bridges_custom": "",
"bridges_builtin": {},
"persistent_tabs": [],
"locale": None, # this gets defined in fill_in_defaults()
"theme": 0,

View File

@ -42,10 +42,11 @@ class SendBaseModeWeb:
self.is_zipped = False
self.download_filename = None
self.download_filesize = None
self.gzip_filename = None
self.gzip_filesize = None
self.zip_writer = None
# Store the tempfile objects here, so when they're garbage collected the files are deleted
self.gzip_files = []
# If autostop_sharing, only allow one download at a time
self.download_in_progress = False
@ -192,12 +193,15 @@ class SendBaseModeWeb:
# gzip compress the individual file, if it hasn't already been compressed
if use_gzip:
if filesystem_path not in self.gzip_individual_files:
gzip_filename = tempfile.mkstemp("wb+")[1]
self._gzip_compress(filesystem_path, gzip_filename, 6, None)
self.gzip_individual_files[filesystem_path] = gzip_filename
self.gzip_files.append(
tempfile.NamedTemporaryFile("wb+", dir=self.common.build_tmp_dir())
)
gzip_file = self.gzip_files[-1]
self._gzip_compress(filesystem_path, gzip_file.name, 6, None)
self.gzip_individual_files[filesystem_path] = gzip_file.name
# Make sure the gzip file gets cleaned up when onionshare stops
self.web.cleanup_filenames.append(gzip_filename)
# Cleanup this temp file
self.web.cleanup_tempfiles.append(gzip_file)
file_to_download = self.gzip_individual_files[filesystem_path]
filesize = os.path.getsize(self.gzip_individual_files[filesystem_path])

View File

@ -134,8 +134,12 @@ class ShareModeWeb(SendBaseModeWeb):
The web app routes for sharing files
"""
@self.web.app.route("/", defaults={"path": ""}, methods=["GET"], provide_automatic_options=False)
@self.web.app.route("/<path:path>", methods=["GET"], provide_automatic_options=False)
@self.web.app.route(
"/", defaults={"path": ""}, methods=["GET"], provide_automatic_options=False
)
@self.web.app.route(
"/<path:path>", methods=["GET"], provide_automatic_options=False
)
def index(path):
"""
Render the template for the onionshare landing page.
@ -159,7 +163,9 @@ class ShareModeWeb(SendBaseModeWeb):
return self.render_logic(path)
@self.web.app.route("/download", methods=["GET"], provide_automatic_options=False)
@self.web.app.route(
"/download", methods=["GET"], provide_automatic_options=False
)
def download():
"""
Download the zip file.
@ -286,7 +292,9 @@ class ShareModeWeb(SendBaseModeWeb):
if if_unmod:
if_date = parse_date(if_unmod)
if if_date and not if_date.tzinfo:
if_date = if_date.replace(tzinfo=timezone.utc) # Compatible with Flask < 2.0.0
if_date = if_date.replace(
tzinfo=timezone.utc
) # Compatible with Flask < 2.0.0
if if_date and if_date > last_modified:
abort(412)
elif range_header is None:
@ -459,7 +467,7 @@ class ShareModeWeb(SendBaseModeWeb):
return self.web.error404(history_id)
def build_zipfile_list(self, filenames, processed_size_callback=None):
self.common.log("ShareModeWeb", "build_zipfile_list")
self.common.log("ShareModeWeb", "build_zipfile_list", f"filenames={filenames}")
for filename in filenames:
info = {
"filename": filename,
@ -484,7 +492,10 @@ class ShareModeWeb(SendBaseModeWeb):
self.download_etag = make_etag(f)
# Compress the file with gzip now, so we don't have to do it on each request
self.gzip_filename = tempfile.mkstemp("wb+")[1]
self.gzip_tmp_dir = tempfile.TemporaryDirectory(
dir=self.common.build_tmp_dir()
)
self.gzip_filename = os.path.join(self.gzip_tmp_dir.name, "file.gz")
self._gzip_compress(
self.download_filename, self.gzip_filename, 6, processed_size_callback
)
@ -492,15 +503,15 @@ class ShareModeWeb(SendBaseModeWeb):
with open(self.gzip_filename, "rb") as f:
self.gzip_etag = make_etag(f)
# Make sure the gzip file gets cleaned up when onionshare stops
self.web.cleanup_filenames.append(self.gzip_filename)
self.is_zipped = False
# Cleanup this tempfile
self.web.cleanup_tempdirs.append(self.gzip_tmp_dir)
else:
# Zip up the files and folders
self.zip_writer = ZipWriter(
self.common, processed_size_callback=processed_size_callback
self.common, self.web, processed_size_callback=processed_size_callback
)
self.download_filename = self.zip_writer.zip_filename
for info in self.file_info["files"]:
@ -519,10 +530,6 @@ class ShareModeWeb(SendBaseModeWeb):
with open(self.download_filename, "rb") as f:
self.download_etag = make_etag(f)
# Make sure the zip file gets cleaned up when onionshare stops
self.web.cleanup_filenames.append(self.zip_writer.zip_filename)
self.web.cleanup_filenames.append(self.zip_writer.zip_temp_dir)
self.is_zipped = True
return True
@ -535,17 +542,24 @@ class ZipWriter(object):
filename.
"""
def __init__(self, common, zip_filename=None, processed_size_callback=None):
def __init__(
self, common, web=None, zip_filename=None, processed_size_callback=None
):
self.common = common
self.web = web
self.cancel_compression = False
if zip_filename:
self.zip_filename = zip_filename
else:
self.zip_temp_dir = tempfile.mkdtemp()
self.zip_filename = (
f"{self.zip_temp_dir}/onionshare_{self.common.random_string(4, 6)}.zip"
self.zip_temp_dir = tempfile.TemporaryDirectory(
dir=self.common.build_tmp_dir()
)
self.zip_filename = f"{self.zip_temp_dir.name}/onionshare_{self.common.random_string(4, 6)}.zip"
# Cleanup this temp dir
if self.web:
self.web.cleanup_tempdirs.append(self.zip_temp_dir)
self.z = zipfile.ZipFile(self.zip_filename, "w", allowZip64=True)
self.processed_size_callback = processed_size_callback

View File

@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import mimetypes
import os
import queue
import requests
@ -80,6 +81,16 @@ class Web:
self.settings = mode_settings
# Flask guesses the MIME type of files from a database on the operating
# system.
# Some operating systems, or applications that can modify the database
# (such as the Windows Registry) can treat .js files as text/plain,
# which breaks the chat app due to X-Content-Type-Options: nosniff.
#
# It's probably #notourbug but we can fix it by forcing the mimetype.
# https://github.com/onionshare/onionshare/issues/1443
mimetypes.add_type("text/javascript", ".js")
# The flask app
self.app = Flask(
__name__,
@ -151,11 +162,17 @@ class Web:
elif self.mode == "website":
self.website_mode = WebsiteModeWeb(self.common, self)
elif self.mode == "chat":
self.socketio = SocketIO()
if self.common.verbose:
self.socketio = SocketIO(
async_mode="gevent", logger=True, engineio_logger=True
)
else:
self.socketio = SocketIO(async_mode="gevent")
self.socketio.init_app(self.app)
self.chat_mode = ChatModeWeb(self.common, self)
self.cleanup_filenames = []
self.cleanup_tempfiles = []
self.cleanup_tempdirs = []
def get_mode(self):
if self.mode == "share":
@ -198,18 +215,19 @@ class Web:
"""
for header, value in self.security_headers:
r.headers.set(header, value)
# Set a CSP header unless in website mode and the user has disabled it
default_csp = "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;"
if self.mode != "website" or (not self.settings.get("website", "disable_csp") and not self.settings.get("website", "custom_csp")):
r.headers.set(
"Content-Security-Policy",
default_csp
)
if self.mode != "website" or (
not self.settings.get("website", "disable_csp")
and not self.settings.get("website", "custom_csp")
):
r.headers.set("Content-Security-Policy", default_csp)
else:
if self.settings.get("website", "custom_csp"):
r.headers.set(
"Content-Security-Policy",
self.settings.get("website", "custom_csp")
self.settings.get("website", "custom_csp"),
)
return r
@ -387,14 +405,13 @@ class Web:
"""
self.common.log("Web", "cleanup")
# Cleanup files
try:
for filename in self.cleanup_filenames:
if os.path.isfile(filename):
os.remove(filename)
elif os.path.isdir(filename):
shutil.rmtree(filename)
except Exception:
# Don't crash if file is still in use
pass
self.cleanup_filenames = []
# Close all of the tempfile.NamedTemporaryFile
for file in self.cleanup_tempfiles:
file.close()
# Clean up the tempfile.NamedTemporaryDirectory objects
for dir in self.cleanup_tempdirs:
dir.cleanup()
self.cleanup_tempfiles = []
self.cleanup_tempdirs = []

217
cli/poetry.lock generated
View File

@ -57,7 +57,7 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
version = "2.0.7"
version = "2.0.9"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
@ -99,7 +99,7 @@ trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"]
[[package]]
name = "eventlet"
version = "0.32.0"
version = "0.33.0"
description = "Highly concurrent networking library"
category = "main"
optional = false
@ -141,6 +141,38 @@ python-versions = "*"
Flask = ">=0.9"
python-socketio = ">=5.0.2"
[[package]]
name = "gevent"
version = "21.12.0"
description = "Coroutine-based network library"
category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5"
[package.dependencies]
cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""}
greenlet = {version = ">=1.1.0,<2.0", markers = "platform_python_implementation == \"CPython\""}
"zope.event" = "*"
"zope.interface" = "*"
[package.extras]
dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"]
docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput", "zope.schema"]
monitor = ["psutil (>=5.7.0)"]
recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "selectors2", "backports.socketpair", "psutil (>=5.7.0)"]
test = ["requests", "objgraph", "cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "selectors2", "futures", "mock", "backports.socketpair", "contextvars (==2.4)", "coverage (>=5.0)", "coveralls (>=1.7.0)", "psutil (>=5.7.0)"]
[[package]]
name = "gevent-websocket"
version = "0.10.1"
description = "Websocket handler for the gevent pywsgi server, a Python network library"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
gevent = "*"
[[package]]
name = "greenlet"
version = "1.1.2"
@ -162,7 +194,7 @@ python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
version = "4.8.2"
version = "4.8.3"
description = "Read metadata from Python packages"
category = "dev"
optional = false
@ -217,14 +249,14 @@ python-versions = ">=3.6"
[[package]]
name = "packaging"
version = "21.2"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3"
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "pluggy"
@ -286,11 +318,14 @@ tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"]
[[package]]
name = "pyparsing"
version = "2.4.7"
version = "3.0.6"
description = "Python parsing module"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
python-versions = ">=3.6"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pysocks"
@ -387,7 +422,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "typing-extensions"
version = "4.0.0"
version = "4.0.1"
description = "Backported and Experimental Type Hints for Python 3.6+"
category = "dev"
optional = false
@ -438,10 +473,35 @@ python-versions = ">=3.6"
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[[package]]
name = "zope.event"
version = "4.5.0"
description = "Very basic event publishing system"
category = "main"
optional = false
python-versions = "*"
[package.extras]
docs = ["sphinx"]
test = ["zope.testrunner"]
[[package]]
name = "zope.interface"
version = "5.4.0"
description = "Interfaces for Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
docs = ["sphinx", "repoze.sphinx.autointerface"]
test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
content-hash = "b6700c9652a3292f2ab3153544c46ed78c75fc9b65c15fcf4e3c243f841565dd"
content-hash = "53661539ae01732f6ff6983cf741121e19eb9f142ed3e8bc43845af7695a2c06"
[metadata.files]
atomicwrites = [
@ -516,8 +576,8 @@ cffi = [
{file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
]
charset-normalizer = [
{file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
{file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
{file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"},
{file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"},
]
click = [
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
@ -532,8 +592,8 @@ dnspython = [
{file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"},
]
eventlet = [
{file = "eventlet-0.32.0-py2.py3-none-any.whl", hash = "sha256:a3a67b02f336e97a1894b277bc33b695831525758781eb024f4da00e75ce5e25"},
{file = "eventlet-0.32.0.tar.gz", hash = "sha256:2f0bb8ed0dc0ab21d683975d5d8ab3c054d588ce61def9faf7a465ee363e839b"},
{file = "eventlet-0.33.0-py2.py3-none-any.whl", hash = "sha256:d10a8fcc9e33381905d9873303fde96ebe3541c03fb795055d2c7347dce0639c"},
{file = "eventlet-0.33.0.tar.gz", hash = "sha256:80144f489c1bb273a51b6f96ff9785a382d2866b9bab1f5bd748385019f4141f"},
]
flask = [
{file = "Flask-1.1.4-py2.py3-none-any.whl", hash = "sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22"},
@ -543,6 +603,45 @@ flask-socketio = [
{file = "Flask-SocketIO-5.0.1.tar.gz", hash = "sha256:5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227"},
{file = "Flask_SocketIO-5.0.1-py2.py3-none-any.whl", hash = "sha256:5d9a4438bafd806c5a3b832e74b69758781a8ee26fb6c9b1dbdda9b4fced432e"},
]
gevent = [
{file = "gevent-21.12.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:2afa3f3ad528155433f6ac8bd64fa5cc303855b97004416ec719a6b1ca179481"},
{file = "gevent-21.12.0-cp27-cp27m-win32.whl", hash = "sha256:177f93a3a90f46a5009e0841fef561601e5c637ba4332ab8572edd96af650101"},
{file = "gevent-21.12.0-cp27-cp27m-win_amd64.whl", hash = "sha256:a5ad4ed8afa0a71e1927623589f06a9b5e8b5e77810be3125cb4d93050d3fd1f"},
{file = "gevent-21.12.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:eae3c46f9484eaacd67ffcdf4eaf6ca830f587edd543613b0f5c4eb3c11d052d"},
{file = "gevent-21.12.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e1899b921219fc8959ff9afb94dae36be82e0769ed13d330a393594d478a0b3a"},
{file = "gevent-21.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c21cb5c9f4e14d75b3fe0b143ec875d7dbd1495fad6d49704b00e57e781ee0f"},
{file = "gevent-21.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:542ae891e2aa217d2cf6d8446538fcd2f3263a40eec123b970b899bac391c47a"},
{file = "gevent-21.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:0082d8a5d23c35812ce0e716a91ede597f6dd2c5ff508a02a998f73598c59397"},
{file = "gevent-21.12.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da8d2d51a49b2a5beb02ad619ca9ddbef806ef4870ba04e5ac7b8b41a5b61db3"},
{file = "gevent-21.12.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfff82f05f14b7f5d9ed53ccb7a609ae8604df522bb05c971bca78ec9d8b2b9"},
{file = "gevent-21.12.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7909780f0cf18a1fc32aafd8c8e130cdd93c6e285b11263f7f2d1a0f3678bc50"},
{file = "gevent-21.12.0-cp36-cp36m-win32.whl", hash = "sha256:bb5cb8db753469c7a9a0b8a972d2660fe851aa06eee699a1ca42988afb0aaa02"},
{file = "gevent-21.12.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c43f081cbca41d27fd8fef9c6a32cf83cb979345b20abc07bf68df165cdadb24"},
{file = "gevent-21.12.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:74fc1ef16b86616cfddcc74f7292642b0f72dde4dd95aebf4c45bb236744be54"},
{file = "gevent-21.12.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc2fef0f98ee180704cf95ec84f2bc2d86c6c3711bb6b6740d74e0afe708b62c"},
{file = "gevent-21.12.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08b4c17064e28f4eb85604486abc89f442c7407d2aed249cf54544ce5c9baee6"},
{file = "gevent-21.12.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:973749bacb7bc4f4181a8fb2a7e0e2ff44038de56d08e856dd54a5ac1d7331b4"},
{file = "gevent-21.12.0-cp37-cp37m-win32.whl", hash = "sha256:6a02a88723ed3f0fd92cbf1df3c4cd2fbd87d82b0a4bac3e36a8875923115214"},
{file = "gevent-21.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f289fae643a3f1c3b909d6b033e6921b05234a4907e9c9c8c3f1fe403e6ac452"},
{file = "gevent-21.12.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:3baeeccc4791ba3f8db27179dff11855a8f9210ddd754f6c9b48e0d2561c2aea"},
{file = "gevent-21.12.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05c5e8a50cd6868dd36536c92fb4468d18090e801bd63611593c0717bab63692"},
{file = "gevent-21.12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d86438ede1cbe0fde6ef4cc3f72bf2f1ecc9630d8b633ff344a3aeeca272cdd"},
{file = "gevent-21.12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01928770972181ad8866ee37ea3504f1824587b188fcab782ef1619ce7538766"},
{file = "gevent-21.12.0-cp38-cp38-win32.whl", hash = "sha256:3c012c73e6c61f13c75e3a4869dbe6a2ffa025f103421a6de9c85e627e7477b1"},
{file = "gevent-21.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:b7709c64afa8bb3000c28bb91ec42c79594a7cb0f322e20427d57f9762366a5b"},
{file = "gevent-21.12.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:ec21f9eaaa6a7b1e62da786132d6788675b314f25f98d9541f1bf00584ed4749"},
{file = "gevent-21.12.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22ce1f38fdfe2149ffe8ec2131ca45281791c1e464db34b3b4321ae9d8d2efbb"},
{file = "gevent-21.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ccffcf708094564e442ac6fde46f0ae9e40015cb69d995f4b39cc29a7643881"},
{file = "gevent-21.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24d3550fbaeef5fddd794819c2853bca45a86c3d64a056a2c268d981518220d1"},
{file = "gevent-21.12.0-cp39-cp39-win32.whl", hash = "sha256:2bcec9f80196c751fdcf389ca9f7141e7b0db960d8465ed79be5e685bfcad682"},
{file = "gevent-21.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:3dad62f55fad839d498c801e139481348991cee6e1c7706041b5fe096cb6a279"},
{file = "gevent-21.12.0-pp27-pypy_73-win_amd64.whl", hash = "sha256:9f9652d1e4062d4b5b5a0a49ff679fa890430b5f76969d35dccb2df114c55e0f"},
{file = "gevent-21.12.0.tar.gz", hash = "sha256:f48b64578c367b91fa793bf8eaaaf4995cb93c8bc45860e473bf868070ad094e"},
]
gevent-websocket = [
{file = "gevent-websocket-0.10.1.tar.gz", hash = "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"},
{file = "gevent_websocket-0.10.1-py3-none-any.whl", hash = "sha256:17b67d91282f8f4c973eba0551183fc84f56f1c90c8f6b6b30256f31f66f5242"},
]
greenlet = [
{file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"},
{file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"},
@ -555,6 +654,7 @@ greenlet = [
{file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"},
{file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"},
{file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"},
{file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"},
{file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"},
{file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"},
{file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"},
@ -567,6 +667,7 @@ greenlet = [
{file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"},
{file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"},
{file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"},
{file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"},
{file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"},
{file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"},
{file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"},
@ -575,6 +676,7 @@ greenlet = [
{file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"},
{file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"},
{file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"},
{file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"},
{file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"},
{file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"},
{file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"},
@ -583,6 +685,7 @@ greenlet = [
{file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"},
{file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"},
{file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"},
{file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"},
{file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"},
{file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"},
{file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"},
@ -591,6 +694,7 @@ greenlet = [
{file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"},
{file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"},
{file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"},
{file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"},
{file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"},
{file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"},
{file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"},
@ -600,8 +704,8 @@ idna = [
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
]
importlib-metadata = [
{file = "importlib_metadata-4.8.2-py3-none-any.whl", hash = "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100"},
{file = "importlib_metadata-4.8.2.tar.gz", hash = "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"},
{file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"},
{file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@ -621,6 +725,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
@ -632,6 +739,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
@ -643,6 +753,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
@ -655,6 +768,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
@ -667,13 +783,16 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
packaging = [
{file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"},
{file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"},
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@ -738,8 +857,8 @@ pynacl = [
{file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"},
]
pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
{file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"},
{file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"},
]
pysocks = [
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
@ -771,7 +890,8 @@ toml = [
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
typing-extensions = [
{file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"},
{file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
{file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
]
unidecode = [
{file = "Unidecode-1.3.2-py3-none-any.whl", hash = "sha256:215fe33c9d1c889fa823ccb66df91b02524eb8cc8c9c80f9c5b8129754d27829"},
@ -789,3 +909,60 @@ zipp = [
{file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
{file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
]
"zope.event" = [
{file = "zope.event-4.5.0-py2.py3-none-any.whl", hash = "sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42"},
{file = "zope.event-4.5.0.tar.gz", hash = "sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"},
]
"zope.interface" = [
{file = "zope.interface-5.4.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7"},
{file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021"},
{file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192"},
{file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a"},
{file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531"},
{file = "zope.interface-5.4.0-cp27-cp27m-win32.whl", hash = "sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325"},
{file = "zope.interface-5.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155"},
{file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"},
{file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959"},
{file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e"},
{file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c"},
{file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702"},
{file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f"},
{file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05"},
{file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004"},
{file = "zope.interface-5.4.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117"},
{file = "zope.interface-5.4.0-cp35-cp35m-win32.whl", hash = "sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8"},
{file = "zope.interface-5.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63"},
{file = "zope.interface-5.4.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f"},
{file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920"},
{file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46"},
{file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc"},
{file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9"},
{file = "zope.interface-5.4.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2"},
{file = "zope.interface-5.4.0-cp36-cp36m-win32.whl", hash = "sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78"},
{file = "zope.interface-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1"},
{file = "zope.interface-5.4.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e"},
{file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b"},
{file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f"},
{file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d"},
{file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8"},
{file = "zope.interface-5.4.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf"},
{file = "zope.interface-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7"},
{file = "zope.interface-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94"},
{file = "zope.interface-5.4.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3"},
{file = "zope.interface-5.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e"},
{file = "zope.interface-5.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7"},
{file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120"},
{file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48"},
{file = "zope.interface-5.4.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4"},
{file = "zope.interface-5.4.0-cp38-cp38-win32.whl", hash = "sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb"},
{file = "zope.interface-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54"},
{file = "zope.interface-5.4.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4"},
{file = "zope.interface-5.4.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d"},
{file = "zope.interface-5.4.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83"},
{file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25"},
{file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1"},
{file = "zope.interface-5.4.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c"},
{file = "zope.interface-5.4.0-cp39-cp39-win32.whl", hash = "sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e"},
{file = "zope.interface-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09"},
{file = "zope.interface-5.4.0.tar.gz", hash = "sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e"},
]

View File

@ -30,6 +30,7 @@ setuptools = "*"
pynacl = "^1.4.0"
colorama = "*"
cepa = "1.8.3"
gevent-websocket = "^0.10.1"
[tool.poetry.dev-dependencies]
pytest = "*"

View File

@ -37,7 +37,7 @@ def temp_dir():
"""Creates a persistent temporary directory for the CLI tests to use"""
global test_temp_dir
if not test_temp_dir:
test_temp_dir = tempfile.mkdtemp()
test_temp_dir = tempfile.TemporaryDirectory()
return test_temp_dir
@ -47,10 +47,9 @@ def temp_dir_1024(temp_dir):
particular size (1024 bytes).
"""
new_temp_dir = tempfile.mkdtemp(dir=temp_dir)
tmp_file, tmp_file_path = tempfile.mkstemp(dir=new_temp_dir)
with open(tmp_file, "wb") as f:
f.write(b"*" * 1024)
new_temp_dir = tempfile.TemporaryDirectory(dir=temp_dir.name)
tmp_file = tempfile.NamedTemporaryFile(dir=new_temp_dir.name)
tmp_file.write(b"*" * 1024)
return new_temp_dir
@ -61,9 +60,8 @@ def temp_dir_1024_delete(temp_dir):
the file inside) will be deleted after fixture usage.
"""
with tempfile.TemporaryDirectory(dir=temp_dir) as new_temp_dir:
tmp_file, tmp_file_path = tempfile.mkstemp(dir=new_temp_dir)
with open(tmp_file, "wb") as f:
with tempfile.TemporaryDirectory(dir=temp_dir.name) as new_temp_dir:
with open(os.path.join(new_temp_dir, "file"), "wb") as f:
f.write(b"*" * 1024)
yield new_temp_dir
@ -72,9 +70,10 @@ def temp_dir_1024_delete(temp_dir):
def temp_file_1024(temp_dir):
"""Create a temporary file of a particular size (1024 bytes)."""
with tempfile.NamedTemporaryFile(delete=False, dir=temp_dir) as tmp_file:
tmp_file.write(b"*" * 1024)
return tmp_file.name
filename = os.path.join(temp_dir.name, "file")
with open(filename, "wb") as f:
f.write(b"*" * 1024)
return filename
@pytest.fixture
@ -84,11 +83,11 @@ def temp_file_1024_delete(temp_dir):
The temporary file will be deleted after fixture usage.
"""
with tempfile.NamedTemporaryFile(dir=temp_dir, delete=False) as tmp_file:
with tempfile.NamedTemporaryFile(dir=temp_dir.name, delete=False) as tmp_file:
tmp_file.write(b"*" * 1024)
tmp_file.flush()
tmp_file.close()
yield tmp_file.name
yield tmp_file
@pytest.fixture(scope="session")

View File

@ -34,6 +34,7 @@ class TestSettings:
"bridges_builtin_pt": "obfs4",
"bridges_moat": "",
"bridges_custom": "",
"bridges_builtin": {},
"persistent_tabs": [],
"theme": 0,
}
@ -54,7 +55,7 @@ class TestSettings:
"socks_port": 9999,
"use_stealth": True,
}
tmp_file, tmp_file_path = tempfile.mkstemp(dir=temp_dir)
tmp_file, tmp_file_path = tempfile.mkstemp(dir=temp_dir.name)
with open(tmp_file, "w") as f:
json.dump(custom_settings, f)
settings_obj.filename = tmp_file_path
@ -69,7 +70,7 @@ class TestSettings:
def test_save(self, monkeypatch, temp_dir, settings_obj):
settings_filename = "default_settings.json"
new_temp_dir = tempfile.mkdtemp(dir=temp_dir)
new_temp_dir = tempfile.mkdtemp(dir=temp_dir.name)
settings_path = os.path.join(new_temp_dir, settings_filename)
settings_obj.filename = settings_path
settings_obj.save()

View File

@ -50,7 +50,8 @@ def web_obj(temp_dir, common_obj, mode, num_files=0):
web = Web(common_obj, False, mode_settings, mode)
web.running = True
web.cleanup_filenames == []
web.cleanup_tempfiles == []
web.cleanup_tempdirs == []
web.app.testing = True
# Share mode
@ -58,7 +59,9 @@ def web_obj(temp_dir, common_obj, mode, num_files=0):
# Add files
files = []
for _ in range(num_files):
with tempfile.NamedTemporaryFile(delete=False, dir=temp_dir) as tmp_file:
with tempfile.NamedTemporaryFile(
delete=False, dir=temp_dir.name
) as tmp_file:
tmp_file.write(b"*" * 1024)
files.append(tmp_file.name)
web.share_mode.set_file_info(files)
@ -131,7 +134,9 @@ class TestWeb:
with web.app.test_client() as c:
# Load / with valid auth
res = c.get("/",)
res = c.get(
"/",
)
res.get_data()
assert res.status_code == 200
@ -169,7 +174,7 @@ class TestWeb:
def test_receive_mode_message_no_files(self, temp_dir, common_obj):
web = web_obj(temp_dir, common_obj, "receive")
data_dir = os.path.join(temp_dir, "OnionShare")
data_dir = os.path.join(temp_dir.name, "OnionShare")
os.makedirs(data_dir, exist_ok=True)
web.settings.set("receive", "data_dir", data_dir)
@ -200,7 +205,7 @@ class TestWeb:
def test_receive_mode_message_and_files(self, temp_dir, common_obj):
web = web_obj(temp_dir, common_obj, "receive")
data_dir = os.path.join(temp_dir, "OnionShare")
data_dir = os.path.join(temp_dir.name, "OnionShare")
os.makedirs(data_dir, exist_ok=True)
web.settings.set("receive", "data_dir", data_dir)
@ -235,7 +240,7 @@ class TestWeb:
def test_receive_mode_files_no_message(self, temp_dir, common_obj):
web = web_obj(temp_dir, common_obj, "receive")
data_dir = os.path.join(temp_dir, "OnionShare")
data_dir = os.path.join(temp_dir.name, "OnionShare")
os.makedirs(data_dir, exist_ok=True)
web.settings.set("receive", "data_dir", data_dir)
@ -267,7 +272,7 @@ class TestWeb:
def test_receive_mode_no_message_no_files(self, temp_dir, common_obj):
web = web_obj(temp_dir, common_obj, "receive")
data_dir = os.path.join(temp_dir, "OnionShare")
data_dir = os.path.join(temp_dir.name, "OnionShare")
os.makedirs(data_dir, exist_ok=True)
web.settings.set("receive", "data_dir", data_dir)
@ -300,15 +305,21 @@ class TestWeb:
res.get_data()
assert res.status_code == 200
def test_cleanup(self, common_obj, temp_dir_1024, temp_file_1024):
def test_cleanup(self, common_obj, temp_dir_1024):
web = web_obj(temp_dir_1024, common_obj, "share", 3)
web.cleanup_filenames = [temp_dir_1024, temp_file_1024]
temp_file = tempfile.NamedTemporaryFile()
temp_dir = tempfile.TemporaryDirectory()
web.cleanup_tempfiles = [temp_file]
web.cleanup_tempdirs = [temp_dir]
web.cleanup()
assert os.path.exists(temp_file_1024) is False
assert os.path.exists(temp_dir_1024) is False
assert web.cleanup_filenames == []
assert os.path.exists(temp_file.name) is False
assert os.path.exists(temp_dir.name) is False
assert web.cleanup_tempfiles == []
assert web.cleanup_tempdirs == []
class TestZipWriterDefault:
@ -339,8 +350,10 @@ class TestZipWriterDefault:
assert default_zw.processed_size_callback(None) is None
def test_add_file(self, default_zw, temp_file_1024_delete):
default_zw.add_file(temp_file_1024_delete)
zipfile_info = default_zw.z.getinfo(os.path.basename(temp_file_1024_delete))
default_zw.add_file(temp_file_1024_delete.name)
zipfile_info = default_zw.z.getinfo(
os.path.basename(temp_file_1024_delete.name)
)
assert zipfile_info.compress_type == zipfile.ZIP_DEFLATED
assert zipfile_info.file_size == 1024
@ -568,7 +581,6 @@ class TestRangeRequests:
resp = client.get(url, headers=headers)
assert resp.status_code == 206
@pytest.mark.skipif(sys.platform != "linux", reason="requires Linux")
@check_unsupported("curl", ["--version"])
def test_curl(self, temp_dir, tmpdir, common_obj):

View File

@ -9,135 +9,83 @@ git clone https://github.com/onionshare/onionshare.git
cd onionshare/desktop
```
Make sure you have Python 3 installed. If you're using Windows or macOS, install version 3.9.9 [from python.org](https://www.python.org/downloads/release/python-399/). For Windows, make sure to install the 32-bit (x86) version, and to check the box to add python to the path on the first page of the installer.
Make sure you have [poetry installed](https://python-poetry.org/docs/#installation), and then install the dependencies:
```sh
poetry install
```
### Install platform-specific dependencies
#### Linux
In Ubuntu 20.04 you need the `libxcb-xinerama0` package installed.
Install python dependencies:
```sh
pip3 install --user poetry requests
```
Download Tor Browser and extract the binaries:
```sh
./scripts/get-tor-linux.py
poetry run ./scripts/get-tor-linux.py
```
#### macOS
Download and install Python 3.8.6 from https://www.python.org/downloads/release/python-386/. I downloaded `python-3.8.6-macosx10.9.pkg`. (You may need to also run `/Applications/Python\ 3.8/Install\ Certificates.command`.)
Install python dependencies:
```sh
pip3 install --user poetry requests
```
Download Tor Browser and extract the binaries:
```sh
./scripts/get-tor-osx.py
poetry run ./scripts/get-tor-osx.py
```
#### Windows
These instructions include adding folders to the path in Windows. To do this, go to Start and type "advanced system settings", and open "View advanced system settings" in the Control Panel. Click Environment Variables. Under "System variables" double-click on Path. From there you can add and remove folders that are available in the PATH.
Download Python 3.8.6, 32-bit (x86) from https://www.python.org/downloads/release/python-386/. I downloaded `python-3.8.6.exe`. When installing it, make sure to check the "Add Python 3.8 to PATH" checkbox on the first page of the installer.
Download and install 7-Zip from http://www.7-zip.org/download.html. I downloaded `7z1900.exe`. Add `C:\Program Files (x86)\7-Zip` to your path.
Install python dependencies:
```
pip install poetry requests
```
Download and install 7-Zip from https://7-zip.org/download.html. I downloaded `7z1900.exe`. Add `C:\Program Files (x86)\7-Zip` to your path.
Download Tor Browser and extract the binaries:
```
python scripts\get-tor-windows.py
```sh
poetry run 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). (In Windows, make sure to install the 32-bit version of Go, such as `go1.17.3.windows-386.msi`.)
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.5.windows-386.msi`.)
Download and compile `meek-client`:
```
./scripts/build-meek-client.py
```
### Prepare the virtual environment
OnionShare uses [Briefcase](https://briefcase.readthedocs.io/en/latest/).
Install Briefcase dependencies by following [these instructions](https://docs.beeware.org/en/latest/tutorial/tutorial-0.html#install-dependencies).
Now create and/or activate a virtual environment.
* Linux and macOS
```
python3 -m venv venv
. venv/bin/activate
```
* Windows
```
python -m venv venv
venv\Scripts\activate.bat
```
While your virtual environment is active, install briefcase from pip.
```
pip install briefcase
```
In order to work with the desktop app, you'll need to build a wheel of the CLI package first, and copy it into the `desktop` folder. You'll need to re-run this script each time you change the CLI code.
```sh
python scripts/rebuild-cli.py
./scripts/build-meek-client.py
```
### Running OnionShare from the source code tree
Inside the virtual environment, run OnionShare like this to install all of the dependencies:
To run OnionShare from the source tree:
```
briefcase dev -d
```sh
poetry run onionshare
poetry run onionshare --help
poetry run onionshare -v
poetry run onionshare -v --local-only
```
Once you have the dependencies installed, you can run it using the `dev.sh` script, which lets you use command line arguments, such as to `--verbose` or `--local-only`:
You can also run `onionshare-cli` from the source tree, and it will look for Tor binaries in `desktop/onionshare/resources/tor`.
```sh
poetry run onionshare-cli --help
```
./scripts/dev.sh --help
./scripts/dev.sh -v
./scripts/dev.sh -v --local-only
```
Windows uses `scripts\dev.bat` instead.
## Running tests
Install these packages inside your virtual environment:
Run the tests:
```sh
pip install pytest pytest-briefcase pytest-faulthandler pytest-qt
```
Then run the tests:
```sh
./tests/run.sh
poetry run ./tests/run.sh
```
If you want to run tests while hiding the GUI, you must have the `xvfb` package installed, and then:
```sh
xvfb-run ./tests/run.sh
xvfb-run poetry run ./tests/run.sh
```

View File

@ -507,5 +507,4 @@ class GuiCommon:
return strings._("error_stealth_not_supported")
elif type(e) is PortNotAvailable:
return strings._("error_port_not_available")
return None

View File

@ -163,6 +163,7 @@ class MainWindow(QtWidgets.QMainWindow):
# Start the "Connecting to Tor" dialog, which calls onion.connect()
tor_con = TorConnectionDialog(self.common)
tor_con.canceled.connect(self.tor_connection_canceled)
tor_con.success.connect(self.tabs.tor_is_connected)
tor_con.open_tor_settings.connect(self.tor_connection_open_tor_settings)
if not self.common.gui.local_only:
tor_con.start()

View File

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 688 B

View File

Before

Width:  |  Height:  |  Size: 203 B

After

Width:  |  Height:  |  Size: 203 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 456 B

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 182 B

After

Width:  |  Height:  |  Size: 182 B

View File

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

View File

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 412 B

View File

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

View File

Before

Width:  |  Height:  |  Size: 754 B

After

Width:  |  Height:  |  Size: 754 B

View File

Before

Width:  |  Height:  |  Size: 435 B

After

Width:  |  Height:  |  Size: 435 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 221 B

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 380 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 347 B

View File

Before

Width:  |  Height:  |  Size: 342 B

After

Width:  |  Height:  |  Size: 342 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 389 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -101,9 +101,9 @@
"gui_settings_connection_type_automatic_option": "Versuche automatische Konfiguration mittels Tor Browser",
"gui_settings_connection_type_test_button": "Verbindung zu Tor testen",
"gui_settings_authenticate_label": "Authentifizierungseinstellungen für Tor",
"gui_settings_tor_bridges": "Unterstützung für Tor-Bridges",
"gui_settings_meek_lite_expensive_warning": "Achtung: Die „meek_lite“-Bridges sind für das Tor-Projekt sehr kostspielig.<br><br> Nutze sie nur, wenn du dich nicht direkt, per obfs4-Transport oder über andere, normale Bridges zum Tor-Netzwerk verbinden kannst.",
"gui_settings_tor_bridges_invalid": "Keine der ausgewählten Bridges funktioniert.\nÜberprüfe sie oder gib andere an.",
"gui_settings_tor_bridges": "Mittels einer Tor-Bridge verbinden?",
"gui_settings_meek_lite_expensive_warning": "Achtung: Die „meek-azure“-Bridges sind für das Tor-Projekt sehr kostspielig.<br><br> Nutze sie nur, wenn du dich nicht direkt, per obfs4-Transport oder über andere, normale Bridges zum Tor-Netzwerk verbinden kannst.",
"gui_settings_tor_bridges_invalid": "Keine der ausgewählten Bridges funktioniert. Überprüfe sie oder gib andere an.",
"settings_error_unknown": "Kann nicht zum Tor-Controller verbinden, weil deine Einstellungen keinen Sinn ergeben.",
"settings_error_automatic": "Kann nicht zum Tor-Controller verbinden. Läuft der Tor Browser (kann von https://www.torproject.org/ heruntergeladen werden) im Hintergrund?",
"settings_error_socket_port": "Kann unter {}:{} nicht zum Tor-Controller verbinden.",
@ -162,7 +162,7 @@
"gui_upload_in_progress": "Upload gestartet {}",
"gui_download_in_progress": "Download gestartet {}",
"gui_open_folder_error_nautilus": "Kann den Ordner nicht öffnen, weil Nautilus nicht verfügbar ist. Die Datei ist hier: {}",
"gui_settings_language_label": "Bevorzugte Sprache",
"gui_settings_language_label": "Sprache",
"gui_settings_language_changed_notice": "Starte OnionShare neu, damit die neue Sprache übernommen wird.",
"help_config": "Ort deiner eigenen JSON Konfigurationsdatei (optional)",
"timeout_upload_still_running": "Warte bis Upload vollständig ist",
@ -316,5 +316,30 @@
"gui_qr_label_url_title": "OnionShare-Adresse",
"gui_copied_client_auth": "Privater Schlüssel in die Zwischenablage kopiert",
"gui_copied_client_auth_title": "Privater Schlüssel kopiert",
"gui_copy_client_auth": "Privaten Schlüssel kopieren"
"gui_copy_client_auth": "Privaten Schlüssel kopieren",
"gui_dragdrop_sandbox_flatpak": "Um die Flatpak Sandbox sicherer zu machen, wird Drag und Drop nicht unterstützt. Bitte nutze stattdessen die Buttons \"Dateien hinzufügen\" und \"Ordner hinzufügen\".",
"gui_tor_settings_window_title": "Tor Einstellungen",
"gui_settings_controller_extras_label": "Tor Einstellungen",
"gui_settings_bridge_use_checkbox": "Benutze eine Brigde",
"gui_settings_bridge_radio_builtin": "Wähle eine eingebaute Bridge",
"gui_settings_bridge_none_radio_option": "Keine Bridge verwenden",
"gui_settings_bridge_moat_button": "Neue Bridge verwenden",
"gui_settings_bridge_custom_placeholder": "Schreibe im Format adresse:port (eine pro Zeile)",
"gui_settings_moat_bridges_invalid": "Du hast noch keine Bridge von torproject.org angefragt.",
"gui_settings_stop_active_tabs_label": "Es laufen noch Services in deinen Tabs.\nDu musst alle Services beenden, bevor du die Tor Einstellungen ändern kannst.",
"gui_settings_version_label": "Du verwendest OnionShare {}",
"gui_settings_help_label": "Du benötigst Hilfe? Gehe zu <a href='https://docs.onionshare.org'>docs.onionshare.org</a>",
"mode_settings_website_custom_csp_checkbox": "Sende einen benutzerdefinierten Content Security Policy header",
"moat_contact_label": "Kontaktiere BridgeDB...",
"moat_captcha_label": "Löse das CAPTCHA um eine Bridge zu laden.",
"moat_captcha_placeholder": "Gib die Zeichen auf dem Bild ein",
"moat_captcha_submit": "Absenden",
"moat_captcha_reload": "Neu laden",
"moat_bridgedb_error": "Fehler beim kontaktieren der BridgeDB.",
"moat_captcha_error": "Die Lösung ist nicht korrekt. Bitte nochmal versuchen.",
"mode_tor_not_connected_label": "OnionShare ist nicht mit dem Tor Netzwerk verbunden",
"gui_settings_bridge_moat_radio_option": "Verwende eine Bridge von torproject.org",
"moat_solution_empty_error": "Du musst die Zeichen auf dem Bild eingeben",
"gui_settings_bridge_custom_radio_option": "Stelle eine Bridge aus einer dir bekannten vertraulichen Quelle bereit",
"gui_settings_tor_bridges_label": "Brigdes helfen dir das Tor Netzwerk an Orten zu verwenden, wo es blockiert wird. Je nachdem wo du bist, funktioniert eine Bridge besser als eine andere."
}

View File

@ -90,16 +90,16 @@
"gui_settings_authenticate_no_auth_option": "Χωρίς επαλήθευση ή επαλήθευση με cookie",
"gui_settings_authenticate_password_option": "Κωδικός",
"gui_settings_password_label": "Κωδικός",
"gui_settings_tor_bridges": "Υποστήριξη γεφυρών Tor",
"gui_settings_tor_bridges": "Σύνδεση με χρήση γέφυρας Tor;",
"gui_settings_tor_bridges_no_bridges_radio_option": "Να μη χρησιμοποιηθούν γέφυρες",
"gui_settings_tor_bridges_obfs4_radio_option": "Να χρησιμοποιηθούν τα ενσωματωμένα obfs4 pluggable transports",
"gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Να χρησιμοποιηθούν τα ενσωματωμένα obfs4 pluggable transports (απαιτείται το πρόγραμμα obfs4proxy)",
"gui_settings_tor_bridges_meek_lite_azure_radio_option": "Να χρησιμοποιηθουν τα ενσωματωμένα meek_lite (Azure) pluggable transports",
"gui_settings_tor_bridges_meek_lite_azure_radio_option_no_obfs4proxy": "Να χρησιμοποιηθούν τα ενσωματωμένα meek_lite (Azure) pluggable transports (απαιτείται το πρόγραμμα obfs4proxy)",
"gui_settings_meek_lite_expensive_warning": "Προσοχή: Τα meek_lite bridges επιβαρύνουν πολύ το Tor Project στη λειτουργία. <br><br> Χρησιμοποιήστε τα μόνο αν δεν μπορείτε να συνδεθείτε κατ' ευθείαν στο Tor μέσω obfs4 transports ή άλλων κανονικών bridges.",
"gui_settings_meek_lite_expensive_warning": "Προσοχή: Οι γέφυρες meek_azure επιβαρύνουν τη λειτουργία του Tor Project. <br><br> Χρησιμοποιήστε τες μόνο αν δεν μπορείτε να συνδεθείτε κατ' ευθείαν στο Tor μέσω obfs4 transports ή άλλων κανονικών γεφυρών.",
"gui_settings_tor_bridges_custom_radio_option": "Χρήση παραμετροποιημένων γεφυρών",
"gui_settings_tor_bridges_custom_label": "Αποκτήστε γέφυρες στο <a href=\"https://bridges.torproject.org/options?lang=el\"> https://bridges.torproject.org</a>",
"gui_settings_tor_bridges_invalid": "Δεν λειτούργησε κάποιο από τα bridges που προσθέσατε.\nΞαναελέγξτε τα ή προσθέστε άλλα.",
"gui_settings_tor_bridges_invalid": "Καμία γέφυρα που προσθέσατε δεν λειτουργεί. Ελέγξτε ξανά ή προσθέστε άλλες.",
"gui_settings_button_save": "Αποθήκευση",
"gui_settings_button_cancel": "Άκυρο",
"gui_settings_button_help": "Βοήθεια",
@ -134,8 +134,8 @@
"gui_server_autostop_timer_expired": "Το χρονόμετρο αυτόματης διακοπής έχει ήδη τελειώσει. Παρακαλώ ρυθμίστε το για να ξεκινήσετε το διαμοιρασμό.",
"share_via_onionshare": "Μοιραστείτε μέσω OnionShare",
"gui_save_private_key_checkbox": "Χρήση μόνιμης διεύθυνσης",
"gui_share_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare μπορεί να <b>κατεβάσει</b> τα αρχεία σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_receive_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare, μπορεί να <b>ανεβάσει</b> αρχεία στον υπολογιστή σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_share_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare και το ιδιωτικό κλειδί μπορεί να <b>κατεβάσει</b> τα αρχεία σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_receive_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare και το ιδιωτικό κλειδί μπορεί να <b>ανεβάσει</b> αρχεία στον υπολογιστή σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_url_label_persistent": "Αυτή η σελίδα διαμοιρασμού δεν θα πάψει να λειτουργεί αυτόματα.<br><br>Όσοι μοιράζονται αρχεία μαζί σας θα μπορέσουν να ξαναχρησιμοποιήσουν αυτή τη διεύθυνση αργότερα. (Για να χρησιμοποιήσετε διευθύνσεις μιας χρήσης, απενεργοποιήστε τη λειτουργία \"Χρήση μόνιμης διεύθυνσης\" στις Ρυθμίσεις.)",
"gui_url_label_stay_open": "Αυτή η σελίδα διαμοιρασμού δεν θα πάψει να λειτουργεί αυτόματα.",
"gui_url_label_onetime": "Αυτός ο διαμοιρασμός θα σταματήσει μετά την πρώτη λήψη.",
@ -178,7 +178,7 @@
"gui_upload_finished": "",
"gui_download_in_progress": "",
"gui_open_folder_error_nautilus": "Δεν μπορεί να ανοιχτεί ο φάκελος γιατί το nautilus δεν είναι διαθέσιμο. Το αρχείο είναι εδω: {}",
"gui_settings_language_label": "Προτιμώμενη γλώσσα",
"gui_settings_language_label": "Γλώσσα",
"gui_settings_language_changed_notice": "Επανεκκινήστε το OnionShare για να εφαρμοστεί η αλλαγή γλώσσας.",
"timeout_upload_still_running": "Αναμονή ολοκλήρωσης του ανεβάσματος",
"gui_add_files": "Προσθήκη αρχείων",
@ -225,7 +225,7 @@
"hours_first_letter": "ώ",
"minutes_first_letter": "λ",
"seconds_first_letter": "δ",
"gui_website_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare μπορεί <b>να επισκεφτεί</b> την ιστοσελίδα χρησιμοποιώντας τον <b>Tor Browser</b>: <img src='{}' />",
"gui_website_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare και το ιδιωτικό κλειδί μπορεί <b>να επισκεφτεί</b> την ιστοσελίδα σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_mode_website_button": "Δημοσίευση ιστοσελίδας",
"gui_website_mode_no_files": "Δεν έχει γίνει διαμοιρασμός ιστοσελίδας ακόμα",
"incorrect_password": "Λάθος κωδικός",
@ -237,14 +237,14 @@
"gui_receive_flatpak_data_dir": "Επειδή έχετε εγκαταστήσει το OnionShare μέσω Flatpak, θα πρέπει να αποθηκεύσετε τα αρχεία μέσα σε έναν φάκελο στο ~/OnionShare.",
"gui_chat_stop_server": "Τερματισμός διακομιστή συνομιλίας",
"gui_chat_start_server": "Έναρξη διακομιστή συνομιλίας",
"mode_settings_website_disable_csp_checkbox": "Μην στέλνετε κεφαλίδα με περιεχόμενο από την πολιτικής ασφάλειας σας (επιτρέπει στην ιστοσελίδα σας να χρησιμοποιεί πόρους τρίτων)",
"mode_settings_website_disable_csp_checkbox": "Μην στέλνετε την προεπιλεγμένη κεφαλίδα Πολιτικής Ασφάλειας Περιεχομένου (επιτρέπει στην ιστοσελίδα σας να χρησιμοποιεί πόρους τρίτων)",
"mode_settings_receive_data_dir_browse_button": "Επιλογή",
"mode_settings_receive_data_dir_label": "Αποθήκευση αρχείων σε",
"mode_settings_share_autostop_sharing_checkbox": "Τερματισμός κοινής χρήσης με την ολοκλήρωση αρχείων (αποεπιλέξτε για λήψη μεμονωμένων αρχείων)",
"mode_settings_legacy_checkbox": "Χρήση παλαιάς διεύθυνσης (δεν προτείνεται η χρήση υπηρεσία v2 onion)",
"mode_settings_autostop_timer_checkbox": "Προγραμματισμένος τερματισμός",
"mode_settings_autostart_timer_checkbox": "Προγραμματισμένη εκκίνηση",
"mode_settings_public_checkbox": "Χωρίς χρήση κωδικού πρόσβασης",
"mode_settings_public_checkbox": "Δημόσια υπηρεσία OnionShare (απενεργοποιεί το ιδιωτικό κλειδί)",
"mode_settings_persistent_checkbox": "Αποθήκευση της καρτέλας και αυτόματο άνοιγμά της με την έναρξη του OnionShare",
"mode_settings_advanced_toggle_hide": "Απόκρυψη προχωρημένων ρυθμίσεων",
"mode_settings_advanced_toggle_show": "Εμφάνιση προχωρημένων ρυθμίσεων",
@ -279,7 +279,7 @@
"error_port_not_available": "Η θύρα OnionShare δεν είναι διαθέσιμη",
"gui_rendezvous_cleanup_quit_early": "Πρόωρη έξοδος",
"gui_rendezvous_cleanup": "Αναμονή για τερματισμό των κυκλωμάτων του Tor για να βεβαιωθείτε ότι τα αρχεία σας έχουν μεταφερθεί με επιτυχία.\n\nΑυτό μπορεί να διαρκέσει λίγα λεπτά.",
"gui_chat_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση του OnionShare μπορεί <b>να συμμετέχει στο δωμάτιο συνομιλίας</b> με χρήση του <b>Tor Browser</b>: <img src='{}' />",
"gui_chat_url_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare και το ιδιωτικό κλειδί μπορεί να <b>συμμετέχει στο δωμάτιο συνομιλίας</b> χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_color_mode_changed_notice": "Επανεκκινήστε το OnionShare για εφαρμοστεί το νέο χρώμα.",
"history_receive_read_message_button": "Ανάγνωση μηνύματος",
"mode_settings_receive_webhook_url_checkbox": "Χρήση ειδοποίησης webhook",
@ -289,5 +289,50 @@
"gui_status_indicator_chat_started": "Σε συνομιλία",
"gui_status_indicator_chat_scheduled": "Δρομολόγηση…",
"gui_status_indicator_chat_working": "Εκκίνηση…",
"gui_status_indicator_chat_stopped": "Έτοιμο για συνομιλία"
"gui_status_indicator_chat_stopped": "Έτοιμο για συνομιλία",
"gui_copied_client_auth_title": "Το ιδιωτικό κλειδί αντιγράφηκε",
"gui_qr_label_url_title": "Διεύθυνση OnionShare",
"gui_reveal": "Εμφάνιση",
"gui_hide": "Απόκρυψη",
"gui_share_url_public_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare μπορεί να <b>κατεβάσει</b> τα αρχεία σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_website_url_public_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare μπορεί <b>να επισκεφθεί</b> την ιστοσελίδα σας χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_chat_url_public_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare μπορεί να <b>συμμετέχει στο δωμάτιο συνομιλίας</b> χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_url_instructions_public_mode": "Στείλτε την παρακάτω διεύθυνση OnionShare:",
"gui_settings_theme_label": "Θέμα",
"gui_copy_client_auth": "Αντιγραφή ιδιωτικού κλειδιού",
"gui_copied_client_auth": "Το ιδιωτικό κλειδί αντιγράφηκε στο πρόχειρο",
"gui_qr_label_auth_string_title": "Ιδιωτικό κλειδί",
"gui_please_wait_no_button": "Εκκίνηση…",
"gui_server_doesnt_support_stealth": "Αυτή η έκδοση Tor, δεν υποστηρίζει το stealth (πιστοποίηση πελάτη). Παρακαλούμε δοκιμάστε με μια νεότερη έκδοση του Tor ή χρησιμοποιήστε τη λειτουργία 'δημόσιο' αν δεν χρειάζεται να είναι ιδιωτική.",
"gui_receive_url_public_description": "<b>Οποιοσδήποτε</b> με αυτή τη διεύθυνση OnionShare μπορεί να <b>ανεβάσει</b> αρχεία στον υπολογιστή σας, χρησιμοποιώντας το <b>Tor Browser</b>: <img src='{}' />",
"gui_settings_theme_auto": "Αυτόματο",
"gui_settings_theme_dark": "Σκοτεινό",
"gui_url_instructions": "Αρχικά, στείλτε την παρακάτω διεύθυνση OnionShare:",
"gui_settings_theme_light": "Φωτεινό",
"gui_client_auth_instructions": "Στη συνέχεια, στείλτε το ιδιωτικό κλειδί για πρόσβαση στην υπηρεσία OnionShare:",
"gui_dragdrop_sandbox_flatpak": "Για να γίνει πιο ασφαλές το sandbox του Flatpak, δεν υποστηρίζεται η μεταφορά και απόθεση. Χρησιμοποιήστε τα κουμπιά Προσθήκη αρχείων και Προσθήκη φακέλου για να αναζητήσετε αρχεία.",
"moat_captcha_label": "Λύστε το CAPTCHA για να αιτηθείτε μια γέφυρα.",
"gui_settings_tor_bridges_label": "Οι γέφυρες σας βοηθούν να αποκτήσετε πρόσβαση στο δίκτυο Tor σε τοποθεσίες όπου είναι αποκλεισμένο. Ανάλογα με το πού βρίσκεστε, μια γέφυρα μπορεί να λειτουργεί καλύτερα από μια άλλη.",
"moat_captcha_submit": "Υποβολή",
"moat_solution_empty_error": "Πρέπει να εισαγάγετε τους χαρακτήρες από την εικόνα",
"moat_contact_label": "Επικοινωνία με BridgeDB...",
"gui_tor_settings_window_title": "Ρυθμίσεις Tor",
"gui_settings_controller_extras_label": "Ρυθμίσεις Tor",
"gui_settings_bridge_use_checkbox": "Χρήση γέφυρας",
"gui_settings_bridge_radio_builtin": "Επιλέξτε μια ενσωματωμένη γέφυρα",
"gui_settings_bridge_none_radio_option": "Χωρίς χρήση γέφυρας",
"gui_settings_bridge_moat_radio_option": "Αιτηθείτε μια γέφυρα από torproject.org",
"gui_settings_bridge_moat_button": "Αίτημα για νέα γέφυρα",
"gui_settings_bridge_custom_radio_option": "Προτείνετε μια γέφυρα για την οποία ενημερωθήκατε από αξιόπιστη πηγή",
"gui_settings_bridge_custom_placeholder": "πληκτρολογήστε διεύθυνση: πόρτα (μια ανά γραμμή)",
"gui_settings_moat_bridges_invalid": "Δεν έχετε αιτηθεί ακόμα γέφυρα από το torproject.org.",
"gui_settings_stop_active_tabs_label": "Υπάρχουν υπηρεσίες που εκτελούνται σε ορισμένες από τις καρτέλες σας.\nΠρέπει να σταματήσετε όλες τις υπηρεσίες για να αλλάξετε τις ρυθμίσεις του Tor.",
"gui_settings_version_label": "Χρησιμοποιείτε το OnionShare {}",
"gui_settings_help_label": "Χρειάζεστε βοήθεια; Δείτε το <a href='https://docs.onionshare.org'>docs.onionshare.org</a>",
"mode_settings_website_custom_csp_checkbox": "Αποστολή προσαρμοσμένης κεφαλίδας Πολιτικής Ασφαλείας Περιεχομένου",
"moat_captcha_placeholder": "Εισαγάγετε τους χαρακτήρες από την εικόνα",
"moat_captcha_reload": "Επαναφόρτωση",
"moat_bridgedb_error": "Σφάλμα επικοινωνίας με BridgeDB.",
"moat_captcha_error": "Η λύση δεν είναι σωστή. Παρακαλούμε δοκιμάστε ξανά.",
"mode_tor_not_connected_label": "Το OnionShare δεν είναι συνδεδεμένο με το δίκτυο Tor"
}

View File

@ -41,8 +41,8 @@
"gui_settings_connection_type_bundled_option": "Usa la versión de Tor incorporada en OnionShare",
"gui_settings_connection_type_automatic_option": "Intentar la configuración automática con el Navegador Tor",
"gui_settings_connection_type_test_button": "Probar la conexión a Tor",
"gui_settings_tor_bridges": "Soporte para puentes Tor",
"gui_settings_tor_bridges_invalid": "No funciona ninguno de los puentes agregados.\nVuelve a comprobarlos o añade otros.",
"gui_settings_tor_bridges": "¿Conectar usando un puente Tor?",
"gui_settings_tor_bridges_invalid": "No funciona ninguno de los puentes que agregaste. Vuelve a comprobarlos o añade otros.",
"settings_saved": "Ajustes guardados en {}",
"give_this_url_receive": "Dele esta dirección al remitente:",
"give_this_url_receive_stealth": "Entrega esta dirección y HidServAuth al remitente:",
@ -143,7 +143,7 @@
"gui_settings_tor_bridges_obfs4_radio_option_no_obfs4proxy": "Usar transportes conectables obfs4 incorporados (requiere obfs4proxy)",
"gui_settings_tor_bridges_meek_lite_azure_radio_option": "Usar transportes conectables incorporados meek_lite (Azure)",
"gui_settings_tor_bridges_meek_lite_azure_radio_option_no_obfs4proxy": "Usar transportes conectables meek_lite (Azure) incorporados (requiere obfs4proxy)",
"gui_settings_meek_lite_expensive_warning": "Advertencia: Los puentes meek_lite son muy costosos de correr para el Proyecto Tor.<br><br>Utilízalos solo si no puedes conectarte a Tor directamente, a través de transportes obfs4 u otros puentes normales.",
"gui_settings_meek_lite_expensive_warning": "Advertencia: Los puentes meek-azure son muy costosos de mantener para el Proyecto Tor.<br><br>Utilízalos solo si no puedes conectarte a Tor directamente, a través de transportes obfs4 u otros puentes normales.",
"gui_settings_tor_bridges_custom_radio_option": "Usar puentes personalizados",
"gui_settings_tor_bridges_custom_label": "Puedes obtener puentes en <a href=\"https://bridges.torproject.org/options\">https://bridges.torproject.org</a>",
"gui_settings_button_save": "Guardar",
@ -177,7 +177,7 @@
"gui_upload_finished": "Subido {}",
"gui_download_in_progress": "Descarga iniciada {}",
"gui_open_folder_error_nautilus": "No se puede abrir la carpeta porque nautilus no está disponible. El archivo está aquí: {}",
"gui_settings_language_label": "Idioma preferido",
"gui_settings_language_label": "Idioma",
"gui_settings_language_changed_notice": "Reinicia OnionShare para que se aplique el idioma nuevo.",
"gui_upload_finished_range": "Cargado {} a {}",
"timeout_upload_still_running": "Esperando a que se complete la subida",
@ -265,7 +265,7 @@
"gui_new_tab_tooltip": "Abrir una pestaña nueva",
"gui_new_tab": "Nueva pestaña",
"gui_new_tab_share_description": "Elige los archivos de tu ordenador para enviarlos a otra persona. La persona o personas a las que quieras enviar los archivos tendrán que usar el Tor Browser para descargarlos de ti.",
"mode_settings_website_disable_csp_checkbox": "No enviar encabezado de Política de Seguridad de Contenido (permite que tu sitio web utilice recursos de terceros)",
"mode_settings_website_disable_csp_checkbox": "No enviar encabezado predeterminado de Política de Seguridad de Contenido (permite que tu sitio web utilice recursos de terceros)",
"mode_settings_receive_data_dir_browse_button": "Navegar",
"mode_settings_receive_data_dir_label": "Guardar archivos en",
"mode_settings_share_autostop_sharing_checkbox": "Dejar de compartir después de haber enviado archivos (desmarcar para permitir la descarga de archivos individuales)",
@ -321,5 +321,30 @@
"gui_settings_theme_dark": "Oscuro",
"gui_settings_theme_light": "Claro",
"gui_settings_theme_auto": "Automático",
"gui_url_instructions_public_mode": "Envíe la siguiente dirección de OnionShare:"
"gui_url_instructions_public_mode": "Envíe la siguiente dirección de OnionShare:",
"gui_dragdrop_sandbox_flatpak": "Para hacer que la zona de prueba de Flatpak sea más segura, arrastrar y colocar no es soportado. En vez, usa los botones Agregar Archivos y Agregar Carpeta para navegar entre archivos.",
"gui_tor_settings_window_title": "Configuraciones de Tor",
"gui_settings_controller_extras_label": "Configuraciones de Tor",
"gui_settings_tor_bridges_label": "Los puentes te ayudan a acceder a la red Tor en lugares donde Tor está bloqueado. Dependiendo de dónde estés, un puente podría funcionar mejor que otro.",
"gui_settings_bridge_use_checkbox": "Usar un puente",
"gui_settings_bridge_radio_builtin": "Seleccionar un puente incorporado",
"gui_settings_bridge_none_radio_option": "No usar un puente",
"gui_settings_bridge_moat_radio_option": "Solicita un puente desde torproject.org",
"gui_settings_bridge_moat_button": "Solicitar un Nuevo Puente",
"gui_settings_bridge_custom_radio_option": "Provee un puente del que te enteraste a través de una fuente confiable",
"gui_settings_bridge_custom_placeholder": "tipea dirección:puerto (una por línea)",
"gui_settings_moat_bridges_invalid": "Aún no has solicitado un puente desde torproject.org.",
"gui_settings_stop_active_tabs_label": "Estos son servicios ejecutándose en algunas de tus pestañas.\nDebes detenerlos a todos para cambiar tus configuraciones de Tor.",
"gui_settings_version_label": "Estás usando OnionShare {}",
"gui_settings_help_label": "¿Necesitas ayuda? Mira <a href='https://docs.onionshare.org'>docs.onionshare.org</a>",
"mode_settings_website_custom_csp_checkbox": "Enviar un encabezado personaizado de Política de Seguridad de Contenido",
"moat_contact_label": "Contactando BridgeDB...",
"moat_captcha_label": "Resuelve el CAPTCHA para solicitar un puente.",
"moat_captcha_placeholder": "Ingresa los caracteres en la imagen",
"moat_captcha_submit": "Enviar",
"moat_captcha_reload": "Recargar",
"moat_bridgedb_error": "Error contactando BridgeDB.",
"moat_captcha_error": "La solución no es correcta. Por favor, inténtalo de nuevo.",
"moat_solution_empty_error": "Debes ingresar los caracteres en la imagen",
"mode_tor_not_connected_label": "OnionShare no está conectado a la red Tor"
}

Some files were not shown because too many files have changed in this diff Show More