mirror of
https://github.com/onionshare/onionshare.git
synced 2025-05-02 14:36:15 -04:00
merge from develop and fix conflicts, update poetry
This commit is contained in:
commit
80cc1f43e8
96 changed files with 3838 additions and 2291 deletions
|
@ -1,24 +1,23 @@
|
|||
```
|
||||
@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ___ _
|
||||
@@@@@@ @@@@@@@@@@@@@ / _ \ (_)
|
||||
@@@@ @ @@@@@@@@@@@ | | | |_ __ _ ___ _ __
|
||||
@@@@@@@@ @@@@@@@@@@ | | | | '_ \| |/ _ \| '_ \
|
||||
@@@@@@@@@@@@ @@@@@@@@@@ \ \_/ / | | | | (_) | | | |
|
||||
@@@@@@@@@@@@@@@@ @@@@@@@@@ \___/|_| |_|_|\___/|_| |_|
|
||||
@@@@@@@@@ @@@@@@@@@@@@@@@@ _____ _
|
||||
@@@@@@@@@@ @@@@@@@@@@@@ / ___| |
|
||||
@@@@@@@@@@ @@@@@@@@ \ `--.| |__ __ _ _ __ ___
|
||||
@@@@@@@@@@@ @ @@@@ `--. \ '_ \ / _` | '__/ _ \
|
||||
@@@@@@@@@@@@@ @@@@@@ /\__/ / | | | (_| | | | __/
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ \____/|_| |_|\__,_|_| \___|
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@
|
||||
╭───────────────────────────────────────────╮
|
||||
│ * ▄▄█████▄▄ * │
|
||||
│ ▄████▀▀▀████▄ * │
|
||||
│ ▀▀█▀ ▀██▄ │
|
||||
│ * ▄█▄ ▀██▄ │
|
||||
│ ▄█████▄ ███ -+- │
|
||||
│ ███ ▀█████▀ │
|
||||
│ ▀██▄ ▀█▀ │
|
||||
│ * ▀██▄ ▄█▄▄ * │
|
||||
│ * ▀████▄▄▄████▀ │
|
||||
│ ▀▀█████▀▀ │
|
||||
│ -+- * │
|
||||
│ ▄▀▄ ▄▀▀ █ │
|
||||
│ █ █ ▀ ▀▄ █ │
|
||||
│ █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ │
|
||||
│ ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ │
|
||||
│ │
|
||||
│ https://onionshare.org/ │
|
||||
╰───────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
## Installing OnionShare CLI
|
||||
|
|
|
@ -18,13 +18,22 @@ 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 os, sys, time, argparse, threading
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
from .common import Common, CannotFindTor
|
||||
from .web import Web
|
||||
from .onion import *
|
||||
from .onion import (
|
||||
TorErrorProtocolError,
|
||||
TorTooOldEphemeral,
|
||||
TorTooOldStealth,
|
||||
Onion,
|
||||
)
|
||||
from .onionshare import OnionShare
|
||||
from .mode_settings import ModeSettings
|
||||
|
||||
|
@ -148,6 +157,18 @@ def main(cwd=None):
|
|||
default=None,
|
||||
help="Receive files: URL to receive webhook notifications",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-text",
|
||||
action="store_true",
|
||||
dest="disable_text",
|
||||
help="Receive files: Disable receiving text messages",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-files",
|
||||
action="store_true",
|
||||
dest="disable_files",
|
||||
help="Receive files: Disable receiving files",
|
||||
)
|
||||
# Website args
|
||||
parser.add_argument(
|
||||
"--disable_csp",
|
||||
|
@ -191,6 +212,8 @@ def main(cwd=None):
|
|||
autostop_sharing = not bool(args.no_autostop_sharing)
|
||||
data_dir = args.data_dir
|
||||
webhook_url = args.webhook_url
|
||||
disable_text = args.disable_text
|
||||
disable_files = args.disable_files
|
||||
disable_csp = bool(args.disable_csp)
|
||||
verbose = bool(args.verbose)
|
||||
|
||||
|
@ -234,6 +257,8 @@ def main(cwd=None):
|
|||
mode_settings.set("receive", "data_dir", data_dir)
|
||||
if webhook_url:
|
||||
mode_settings.set("receive", "webhook_url", webhook_url)
|
||||
mode_settings.set("receive", "disable_text", disable_text)
|
||||
mode_settings.set("receive", "disable_files", disable_files)
|
||||
if mode == "website":
|
||||
mode_settings.set("website", "disable_csp", disable_csp)
|
||||
else:
|
||||
|
@ -276,6 +301,11 @@ def main(cwd=None):
|
|||
if persistent_filename:
|
||||
mode_settings.set(mode, "filenames", filenames)
|
||||
|
||||
# In receive mode, you must allows either text, files, or both
|
||||
if mode == "receive" and disable_text and disable_files:
|
||||
print("You cannot disable both text and files")
|
||||
sys.exit()
|
||||
|
||||
# Create the Web object
|
||||
web = Web(common, False, mode_settings, mode)
|
||||
|
||||
|
@ -299,7 +329,7 @@ def main(cwd=None):
|
|||
except KeyboardInterrupt:
|
||||
print("")
|
||||
sys.exit()
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
sys.exit()
|
||||
|
||||
# Start the onionshare app
|
||||
|
@ -343,7 +373,9 @@ def main(cwd=None):
|
|||
)
|
||||
print("")
|
||||
print(
|
||||
"Warning: Receive mode lets people upload files to your computer. Some files can potentially take control of your computer if you open them. Only open things from people you trust, or if you know what you are doing."
|
||||
"Warning: Receive mode lets people upload files to your computer. Some files can potentially take "
|
||||
"control of your computer if you open them. Only open things from people you trust, or if you know "
|
||||
"what you are doing."
|
||||
)
|
||||
print("")
|
||||
if mode_settings.get("general", "client_auth"):
|
||||
|
@ -394,7 +426,6 @@ def main(cwd=None):
|
|||
print("Compressing files.")
|
||||
try:
|
||||
web.share_mode.set_file_info(filenames)
|
||||
app.cleanup_filenames += web.share_mode.cleanup_filenames
|
||||
except OSError as e:
|
||||
print(e.strerror)
|
||||
sys.exit(1)
|
||||
|
@ -437,7 +468,9 @@ def main(cwd=None):
|
|||
)
|
||||
print("")
|
||||
print(
|
||||
"Warning: Receive mode lets people upload files to your computer. Some files can potentially take control of your computer if you open them. Only open things from people you trust, or if you know what you are doing."
|
||||
"Warning: Receive mode lets people upload files to your computer. Some files can potentially take "
|
||||
"control of your computer if you open them. Only open things from people you trust, or if you know "
|
||||
"what you are doing."
|
||||
)
|
||||
print("")
|
||||
|
||||
|
@ -486,7 +519,7 @@ def main(cwd=None):
|
|||
web.stop(app.port)
|
||||
finally:
|
||||
# Shutdown
|
||||
app.cleanup()
|
||||
web.cleanup()
|
||||
onion.cleanup()
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ import time
|
|||
import shutil
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
import colorama
|
||||
from colorama import Fore, Back, Style
|
||||
|
||||
from .settings import Settings
|
||||
|
||||
|
||||
|
@ -43,18 +46,11 @@ class Common:
|
|||
The Common object is shared amongst all parts of OnionShare.
|
||||
"""
|
||||
|
||||
C_RESET = "\033[0m"
|
||||
C_BG_PURPLE = "\033[45m"
|
||||
C_BOLD = "\033[01m"
|
||||
C_WHITE = "\033[97m"
|
||||
C_LIGHTGRAY = "\033[37m"
|
||||
C_DARKGRAY = "\033[90m"
|
||||
C_LIGHTPURPLE = "\033[95m"
|
||||
C_DARKPURPLE = "\033[35m"
|
||||
|
||||
def __init__(self, verbose=False):
|
||||
self.verbose = verbose
|
||||
|
||||
colorama.init(autoreset=True)
|
||||
|
||||
# The platform OnionShare is running on
|
||||
self.platform = platform.system()
|
||||
if self.platform.endswith("BSD") or self.platform == "DragonFly":
|
||||
|
@ -90,219 +86,195 @@ class Common:
|
|||
╰───────────────────────────────────────────╯
|
||||
"""
|
||||
|
||||
if self.platform == "Windows":
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
+ "╭───────────────────────────────────────────╮"
|
||||
+ self.C_RESET
|
||||
Back.MAGENTA + Fore.WHITE + "╭───────────────────────────────────────────╮"
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ "▄▄█████▄▄"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▄████▀▀▀████▄"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█▀ ▀██▄ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ "▄█▄ ▀██▄ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▄█████▄ ███"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ███ ▀█████▀ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▀██▄ ▀█▀ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ "▀██▄ ▄█▄▄"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " * "
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ "▀████▄▄▄████▀ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▀▀█████▀▀ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTPURPLE
|
||||
+ Fore.LIGHTMAGENTA_EX
|
||||
+ " -+- * "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▄▀▄ ▄▀▀ █ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " █ █ ▀ ▀▄ █ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " █ █ █▀▄ █ ▄▀▄ █▀▄ ▀▄ █▀▄ ▄▀▄ █▄▀ ▄█▄ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_WHITE
|
||||
+ Fore.WHITE
|
||||
+ " ▀▄▀ █ █ █ ▀▄▀ █ █ ▄▄▀ █ █ ▀▄█ █ ▀▄▄ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
+ "│ │"
|
||||
+ self.C_RESET
|
||||
Back.MAGENTA + Fore.WHITE + "│ │"
|
||||
)
|
||||
left_spaces = (43 - len(self.version) - 1) // 2
|
||||
right_spaces = left_spaces
|
||||
if left_spaces + len(self.version) + right_spaces < 43:
|
||||
right_spaces += 1
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ f"{' '*left_spaces}v{self.version}{' '*right_spaces}"
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
+ "│ │"
|
||||
+ self.C_RESET
|
||||
Back.MAGENTA + Fore.WHITE + "│ │"
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
Back.MAGENTA
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ " https://onionshare.org/ "
|
||||
+ self.C_LIGHTGRAY
|
||||
+ Fore.WHITE
|
||||
+ "│"
|
||||
+ self.C_RESET
|
||||
)
|
||||
print(
|
||||
self.C_BG_PURPLE
|
||||
+ self.C_LIGHTGRAY
|
||||
+ "╰───────────────────────────────────────────╯"
|
||||
+ self.C_RESET
|
||||
Back.MAGENTA + Fore.WHITE + "╰───────────────────────────────────────────╯"
|
||||
)
|
||||
print()
|
||||
|
||||
|
@ -319,9 +291,11 @@ class Common:
|
|||
"""
|
||||
if self.verbose:
|
||||
timestamp = time.strftime("%b %d %Y %X")
|
||||
final_msg = f"{self.C_DARKGRAY}[{timestamp}]{self.C_RESET} {self.C_LIGHTGRAY}{module}.{func}{self.C_RESET}"
|
||||
final_msg = f"{Fore.LIGHTBLACK_EX + Style.DIM}[{timestamp}]{Style.RESET_ALL} {Fore.WHITE + Style.DIM}{module}.{func}{Style.RESET_ALL}"
|
||||
if msg:
|
||||
final_msg = f"{final_msg}{self.C_LIGHTGRAY}: {msg}{self.C_RESET}"
|
||||
final_msg = (
|
||||
f"{final_msg}{Fore.WHITE + Style.DIM}: {msg}{Style.RESET_ALL}"
|
||||
)
|
||||
print(final_msg)
|
||||
|
||||
def get_resource_path(self, filename):
|
||||
|
@ -383,7 +357,7 @@ class Common:
|
|||
try:
|
||||
xdg_config_home = os.environ["XDG_CONFIG_HOME"]
|
||||
onionshare_data_dir = f"{xdg_config_home}/onionshare"
|
||||
except:
|
||||
except Exception:
|
||||
onionshare_data_dir = os.path.expanduser("~/.config/onionshare")
|
||||
elif self.platform == "Darwin":
|
||||
onionshare_data_dir = os.path.expanduser(
|
||||
|
@ -393,7 +367,7 @@ class Common:
|
|||
try:
|
||||
xdg_config_home = os.environ["XDG_CONFIG_HOME"]
|
||||
onionshare_data_dir = f"{xdg_config_home}/onionshare"
|
||||
except:
|
||||
except Exception:
|
||||
onionshare_data_dir = os.path.expanduser("~/.config/onionshare")
|
||||
|
||||
# Modify the data dir if running tests
|
||||
|
|
|
@ -55,6 +55,8 @@ class ModeSettings:
|
|||
"receive": {
|
||||
"data_dir": self.build_default_receive_data_dir(),
|
||||
"webhook_url": None,
|
||||
"disable_text": False,
|
||||
"disable_files": False,
|
||||
},
|
||||
"website": {"disable_csp": False, "filenames": []},
|
||||
"chat": {"room": "default"},
|
||||
|
@ -128,7 +130,7 @@ class ModeSettings:
|
|||
self.fill_in_defaults()
|
||||
self.common.log("ModeSettings", "load", f"loaded {self.filename}")
|
||||
return
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If loading settings didn't work, create the settings file
|
||||
|
|
|
@ -236,7 +236,7 @@ class Onion(object):
|
|||
)
|
||||
try:
|
||||
self.tor_socks_port = self.common.get_available_port(1000, 65535)
|
||||
except:
|
||||
except Exception:
|
||||
print("OnionShare port not available")
|
||||
raise PortNotAvailable()
|
||||
self.tor_torrc = os.path.join(self.tor_data_directory_name, "torrc")
|
||||
|
@ -258,7 +258,7 @@ class Onion(object):
|
|||
proc.terminate()
|
||||
proc.wait()
|
||||
break
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self.common.platform == "Windows" or self.common.platform == "Darwin":
|
||||
|
@ -269,7 +269,7 @@ class Onion(object):
|
|||
torrc_template += "ControlPort {{control_port}}\n"
|
||||
try:
|
||||
self.tor_control_port = self.common.get_available_port(1000, 65535)
|
||||
except:
|
||||
except Exception:
|
||||
print("OnionShare port not available")
|
||||
raise PortNotAvailable()
|
||||
self.tor_control_socket = None
|
||||
|
@ -442,7 +442,7 @@ class Onion(object):
|
|||
try:
|
||||
self.c = Controller.from_port(port=int(env_port))
|
||||
found_tor = True
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
else:
|
||||
|
@ -452,7 +452,7 @@ class Onion(object):
|
|||
for port in ports:
|
||||
self.c = Controller.from_port(port=port)
|
||||
found_tor = True
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If this still didn't work, try guessing the default socket file path
|
||||
|
@ -466,7 +466,7 @@ class Onion(object):
|
|||
|
||||
self.c = Controller.from_socket_file(path=socket_file_path)
|
||||
found_tor = True
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If connecting to default control ports failed, so let's try
|
||||
|
@ -488,14 +488,14 @@ class Onion(object):
|
|||
|
||||
self.c = Controller.from_socket_file(path=socket_file_path)
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
print(automatic_error)
|
||||
raise TorErrorAutomatic()
|
||||
|
||||
# Try authenticating
|
||||
try:
|
||||
self.c.authenticate()
|
||||
except:
|
||||
except Exception:
|
||||
print(automatic_error)
|
||||
raise TorErrorAutomatic()
|
||||
|
||||
|
@ -518,7 +518,7 @@ class Onion(object):
|
|||
print(invalid_settings_error)
|
||||
raise TorErrorInvalidSetting()
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
if self.settings.get("connection_type") == "control_port":
|
||||
print(
|
||||
"Can't connect to the Tor controller at {}:{}.".format(
|
||||
|
@ -597,8 +597,8 @@ class Onion(object):
|
|||
tmp_service_id = res.service_id
|
||||
self.c.remove_ephemeral_hidden_service(tmp_service_id)
|
||||
self.supports_stealth = True
|
||||
except:
|
||||
# ephemeral v3 stealth onion services are not supported
|
||||
except Exception:
|
||||
# ephemeral stealth onion services are not supported
|
||||
self.supports_stealth = False
|
||||
|
||||
# Does this version of Tor support next-gen ('v3') onions?
|
||||
|
@ -716,7 +716,7 @@ class Onion(object):
|
|||
self.c.remove_ephemeral_hidden_service(
|
||||
mode_settings.get("general", "service_id")
|
||||
)
|
||||
except:
|
||||
except Exception:
|
||||
self.common.log(
|
||||
"Onion", "stop_onion_service", f"failed to remove {onion_host}"
|
||||
)
|
||||
|
@ -737,12 +737,12 @@ class Onion(object):
|
|||
"Onion", "cleanup", f"trying to remove onion {onion_host}"
|
||||
)
|
||||
self.c.remove_ephemeral_hidden_service(service_id)
|
||||
except:
|
||||
except Exception:
|
||||
self.common.log(
|
||||
"Onion", "cleanup", f"failed to remove onion {onion_host}"
|
||||
)
|
||||
pass
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if stop_tor:
|
||||
|
@ -785,7 +785,7 @@ class Onion(object):
|
|||
)
|
||||
symbols_i = (symbols_i + 1) % len(symbols)
|
||||
time.sleep(1)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.tor_proc.terminate()
|
||||
|
@ -805,7 +805,7 @@ class Onion(object):
|
|||
"cleanup",
|
||||
"Tried to kill tor process but it's still running",
|
||||
)
|
||||
except:
|
||||
except Exception:
|
||||
self.common.log(
|
||||
"Onion", "cleanup", "Exception while killing tor process"
|
||||
)
|
||||
|
@ -818,7 +818,7 @@ class Onion(object):
|
|||
# Delete the temporary tor data directory
|
||||
if self.use_tmp_dir:
|
||||
self.tor_data_directory.cleanup()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def get_tor_socks_port(self):
|
||||
|
|
|
@ -18,7 +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 os, shutil
|
||||
import os
|
||||
from .common import AutoStopTimer
|
||||
|
||||
|
||||
|
@ -57,7 +57,7 @@ class OnionShare(object):
|
|||
"""
|
||||
try:
|
||||
self.port = self.common.get_available_port(17600, 17650)
|
||||
except:
|
||||
except Exception:
|
||||
raise OSError("Cannot find an available OnionShare port")
|
||||
|
||||
def start_onion_service(self, mode, mode_settings, await_publication=True):
|
||||
|
@ -88,21 +88,3 @@ class OnionShare(object):
|
|||
Stop the onion service
|
||||
"""
|
||||
self.onion.stop_onion_service(mode_settings)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Shut everything down and clean up temporary files, etc.
|
||||
"""
|
||||
self.common.log("OnionShare", "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:
|
||||
# Don't crash if file is still in use
|
||||
pass
|
||||
self.cleanup_filenames = []
|
||||
|
|
|
@ -285,6 +285,13 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
|
|||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.upload-wrapper textarea {
|
||||
max-width: 95%;
|
||||
width: 600px;
|
||||
height: 150px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
div#uploads {
|
||||
width: 800px;
|
||||
max-width: 90%;
|
||||
|
|
|
@ -1,24 +1,35 @@
|
|||
$(function(){
|
||||
$(function () {
|
||||
// Add a flash message
|
||||
var flash = function(category, message) {
|
||||
var flash = function (category, message) {
|
||||
$('#flashes').append($('<li>').addClass(category).text(message));
|
||||
};
|
||||
|
||||
var scriptSrc = document.getElementById('receive-script').src;
|
||||
var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf( '/' )+1).replace('js', 'img');
|
||||
var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf('/') + 1).replace('js', 'img');
|
||||
|
||||
// Intercept submitting the form
|
||||
$('#send').submit(function(event){
|
||||
$('#send').submit(function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
// Create form data, and list of filenames
|
||||
var files = $('#file-select').get(0).files;
|
||||
var filenames = [];
|
||||
// Build the form data
|
||||
var formData = new FormData();
|
||||
for(var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
filenames.push(file.name);
|
||||
formData.append('file[]', file, file.name);
|
||||
|
||||
// Files
|
||||
var filenames = [];
|
||||
var $fileSelect = $('#file-select');
|
||||
if ($fileSelect.length > 0) {
|
||||
var files = $fileSelect.get(0).files;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
filenames.push(file.name);
|
||||
formData.append('file[]', file, file.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Text message
|
||||
var $text = $('#text');
|
||||
if ($text.length > 0) {
|
||||
formData.append("text", $text.val())
|
||||
}
|
||||
|
||||
// Reset the upload form
|
||||
|
@ -28,9 +39,9 @@ $(function(){
|
|||
// have access to the the XMLHttpRequest object
|
||||
var ajax = new XMLHttpRequest();
|
||||
|
||||
ajax.upload.addEventListener('progress', function(event){
|
||||
ajax.upload.addEventListener('progress', function (event) {
|
||||
// Update progress bar for this specific upload
|
||||
if(event.lengthComputable) {
|
||||
if (event.lengthComputable) {
|
||||
$('progress', ajax.$upload_div).attr({
|
||||
value: event.loaded,
|
||||
max: event.total,
|
||||
|
@ -39,13 +50,13 @@ $(function(){
|
|||
|
||||
// If it's finished sending all data to the first Tor node, remove cancel button
|
||||
// and update the status
|
||||
if(event.loaded == event.total) {
|
||||
if (event.loaded == event.total) {
|
||||
$('.cancel', ajax.$upload_div).remove();
|
||||
$('.upload-status', ajax.$upload_div).html('<img src="' + staticImgPath + '/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...');
|
||||
}
|
||||
}, false);
|
||||
|
||||
ajax.addEventListener('load', function(event){
|
||||
ajax.addEventListener('load', function (event) {
|
||||
// Remove the upload div
|
||||
ajax.$upload_div.remove();
|
||||
|
||||
|
@ -54,38 +65,38 @@ $(function(){
|
|||
var response = JSON.parse(ajax.response);
|
||||
|
||||
// The 'new_body' response replaces the whole HTML document and ends
|
||||
if('new_body' in response) {
|
||||
if ('new_body' in response) {
|
||||
$('body').html(response['new_body']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show error flashes
|
||||
if('error_flashes' in response) {
|
||||
for(var i=0; i<response['error_flashes'].length; i++) {
|
||||
if ('error_flashes' in response) {
|
||||
for (var i = 0; i < response['error_flashes'].length; i++) {
|
||||
flash('error', response['error_flashes'][i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Show info flashes
|
||||
if('info_flashes' in response) {
|
||||
for(var i=0; i<response['info_flashes'].length; i++) {
|
||||
if ('info_flashes' in response) {
|
||||
for (var i = 0; i < response['info_flashes'].length; i++) {
|
||||
flash('info', response['info_flashes'][i]);
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
flash('error', 'Invalid response from server: '+data);
|
||||
} catch (e) {
|
||||
flash('error', 'Invalid response from server: ' + data);
|
||||
}
|
||||
}, false);
|
||||
|
||||
ajax.addEventListener('error', function(event){
|
||||
flash('error', 'Error uploading: '+filenames.join(', '));
|
||||
ajax.addEventListener('error', function (event) {
|
||||
flash('error', 'Error uploading: ' + filenames.join(', '));
|
||||
|
||||
// Remove the upload div
|
||||
ajax.$upload_div.remove()
|
||||
}, false);
|
||||
|
||||
ajax.addEventListener('abort', function(event){
|
||||
flash('error', 'Upload aborted: '+filenames.join(', '));
|
||||
ajax.addEventListener('abort', function (event) {
|
||||
flash('error', 'Upload aborted: ' + filenames.join(', '));
|
||||
}, false);
|
||||
|
||||
// Make the upload div
|
||||
|
@ -114,7 +125,7 @@ $(function(){
|
|||
)
|
||||
.append($progress);
|
||||
|
||||
$cancel_button.click(function(){
|
||||
$cancel_button.click(function () {
|
||||
// Abort the upload, and remove the upload div
|
||||
ajax.abort();
|
||||
$upload_div.remove()
|
||||
|
|
|
@ -19,8 +19,18 @@
|
|||
<div class="upload-wrapper">
|
||||
<p><img class="logo" src="{{ static_url_path }}/img/logo_large.png" title="OnionShare"></p>
|
||||
|
||||
<p class="upload-header">Send Files</p>
|
||||
<p class="upload-description">Select the files you want to send, then click "Send Files"...</p>
|
||||
{% if not disable_text and not disable_files %}
|
||||
<p class="upload-header">Submit Files or Messages</p>
|
||||
<p class="upload-description">You can submit files, a message, or both</p>
|
||||
{% endif %}
|
||||
{% if not disable_text and disable_files %}
|
||||
<p class="upload-header">Submit Messages</p>
|
||||
<p class="upload-description">You can submit a message</p>
|
||||
{% endif %}
|
||||
{% if disable_text and not disable_files %}
|
||||
<p class="upload-header">Submit Files</p>
|
||||
<p class="upload-description">You can submit files</p>
|
||||
{% endif %}
|
||||
|
||||
<div id="uploads"></div>
|
||||
|
||||
|
@ -37,8 +47,13 @@
|
|||
</div>
|
||||
|
||||
<form id="send" method="post" enctype="multipart/form-data" action="/upload">
|
||||
{% if not disable_files %}
|
||||
<p><input type="file" id="file-select" name="file[]" multiple /></p>
|
||||
<p><button type="submit" id="send-button" class="button">Send Files</button></p>
|
||||
{% endif %}
|
||||
{% if not disable_text %}
|
||||
<p><textarea id="text" name="text" placeholder="Write a message"></textarea></p>
|
||||
{% endif %}
|
||||
<p><button type="submit" id="send-button" class="button">Submit</button></p>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
Bridge obfs4 154.35.22.10:80 8FB9F4319E89E5C6223052AA525A192AFBC85D55 cert=GGGS1TX4R81m3r0HBl79wKy1OtPPNR2CZUIrHjkRg65Vc2VR8fOyo64f9kmT1UAFG7j0HQ iat-mode=0
|
||||
Bridge obfs4 83.212.101.3:50002 A09D536DD1752D542E1FBB3C9CE4449D51298239 cert=lPRQ/MXdD1t5SRZ9MquYQNT9m5DV757jtdXdlePmRCudUU9CFUOX1Tm7/meFSyPOsud7Cw iat-mode=0
|
||||
Bridge obfs4 109.105.109.165:10527 8DFCD8FB3285E855F5A55EDDA35696C743ABFC4E cert=Bvg/itxeL4TWKLP6N1MaQzSOC6tcRIBv6q57DYAZc3b2AzuM+/TfB7mqTFEfXILCjEwzVA iat-mode=1
|
||||
Bridge obfs4 154.35.22.11:80 A832D176ECD5C7C6B58825AE22FC4C90FA249637 cert=YPbQqXPiqTUBfjGFLpm9JYEFTBvnzEJDKJxXG5Sxzrr/v2qrhGU4Jls9lHjLAhqpXaEfZw iat-mode=0
|
||||
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
|
||||
Bridge obfs4 154.35.22.9:443 C73ADBAC8ADFDBF0FC0F3F4E8091C0107D093716 cert=gEGKc5WN/bSjFa6UkG9hOcft1tuK+cV8hbZ0H6cqXiMPLqSbCh2Q3PHe5OOr6oMVORhoJA iat-mode=0
|
||||
Bridge obfs4 154.35.22.11:443 A832D176ECD5C7C6B58825AE22FC4C90FA249637 cert=YPbQqXPiqTUBfjGFLpm9JYEFTBvnzEJDKJxXG5Sxzrr/v2qrhGU4Jls9lHjLAhqpXaEfZw iat-mode=0
|
||||
Bridge obfs4 154.35.22.13:443 FE7840FE1E21FE0A0639ED176EDA00A3ECA1E34D cert=fKnzxr+m+jWXXQGCaXe4f2gGoPXMzbL+bTBbXMYXuK0tMotd+nXyS33y2mONZWU29l81CA iat-mode=0
|
||||
Bridge obfs4 154.35.22.10:443 8FB9F4319E89E5C6223052AA525A192AFBC85D55 cert=GGGS1TX4R81m3r0HBl79wKy1OtPPNR2CZUIrHjkRg65Vc2VR8fOyo64f9kmT1UAFG7j0HQ iat-mode=0
|
||||
Bridge obfs4 154.35.22.9:80 C73ADBAC8ADFDBF0FC0F3F4E8091C0107D093716 cert=gEGKc5WN/bSjFa6UkG9hOcft1tuK+cV8hbZ0H6cqXiMPLqSbCh2Q3PHe5OOr6oMVORhoJA iat-mode=0
|
||||
Bridge obfs4 192.99.11.54:443 7B126FAB960E5AC6A629C729434FF84FB5074EC2 cert=VW5f8+IBUWpPFxF+rsiVy2wXkyTQG7vEd+rHeN2jV5LIDNu8wMNEOqZXPwHdwMVEBdqXEw iat-mode=0
|
||||
Bridge obfs4 154.35.22.13:16815 FE7840FE1E21FE0A0639ED176EDA00A3ECA1E34D cert=fKnzxr+m+jWXXQGCaXe4f2gGoPXMzbL+bTBbXMYXuK0tMotd+nXyS33y2mONZWU29l81CA iat-mode=0
|
||||
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
|
||||
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
|
||||
Bridge obfs4 154.35.22.11:16488 A832D176ECD5C7C6B58825AE22FC4C90FA249637 cert=YPbQqXPiqTUBfjGFLpm9JYEFTBvnzEJDKJxXG5Sxzrr/v2qrhGU4Jls9lHjLAhqpXaEfZw iat-mode=0
|
||||
Bridge obfs4 154.35.22.9:12166 C73ADBAC8ADFDBF0FC0F3F4E8091C0107D093716 cert=gEGKc5WN/bSjFa6UkG9hOcft1tuK+cV8hbZ0H6cqXiMPLqSbCh2Q3PHe5OOr6oMVORhoJA iat-mode=0
|
||||
Bridge obfs4 109.105.109.147:13764 BBB28DF0F201E706BE564EFE690FE9577DD8386D cert=KfMQN/tNMFdda61hMgpiMI7pbwU1T+wxjTulYnfw+4sgvG0zSH7N7fwT10BI8MUdAD7iJA iat-mode=2
|
||||
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
|
||||
Bridge obfs4 [2001:470:b381:bfff:216:3eff:fe23:d6c3]:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
|
||||
Bridge obfs4 85.17.30.79:443 FC259A04A328A07FED1413E9FC6526530D9FD87A cert=RutxZlu8BtyP+y0NX7bAVD41+J/qXNhHUrKjFkRSdiBAhIHIQLhKQ2HxESAKZprn/lR3KA iat-mode=0
|
||||
Bridge obfs4 154.35.22.10:15937 8FB9F4319E89E5C6223052AA525A192AFBC85D55 cert=GGGS1TX4R81m3r0HBl79wKy1OtPPNR2CZUIrHjkRg65Vc2VR8fOyo64f9kmT1UAFG7j0HQ iat-mode=0
|
||||
Bridge obfs4 37.218.240.34:40035 88CD36D45A35271963EF82E511C8827A24730913 cert=eGXYfWODcgqIdPJ+rRupg4GGvVGfh25FWaIXZkit206OSngsp7GAIiGIXOJJROMxEqFKJg iat-mode=1
|
||||
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
|
||||
Bridge obfs4 154.35.22.12:80 00DC6C4FA49A65BD1472993CF6730D54F11E0DBB cert=N86E9hKXXXVz6G7w2z8wFfhIDztDAzZ/3poxVePHEYjbKDWzjkRDccFMAnhK75fc65pYSg 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 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 154.35.22.12:4304 00DC6C4FA49A65BD1472993CF6730D54F11E0DBB cert=N86E9hKXXXVz6G7w2z8wFfhIDztDAzZ/3poxVePHEYjbKDWzjkRDccFMAnhK75fc65pYSg iat-mode=0
|
||||
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
|
||||
Bridge obfs4 144.217.20.138:80 FB70B257C162BF1038CA669D568D76F5B7F0BABB cert=vYIV5MgrghGQvZPIi1tJwnzorMgqgmlKaB77Y3Z9Q/v94wZBOAXkW+fdx4aSxLVnKO+xNw iat-mode=0
|
||||
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 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
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.3.1
|
||||
2.3.2.dev1
|
|
@ -22,12 +22,6 @@ import json
|
|||
import os
|
||||
import locale
|
||||
|
||||
try:
|
||||
# We only need pwd module in macOS, and it's not available in Windows
|
||||
import pwd
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class Settings(object):
|
||||
"""
|
||||
|
@ -166,13 +160,13 @@ class Settings(object):
|
|||
with open(self.filename, "r") as f:
|
||||
self._settings = json.load(f)
|
||||
self.fill_in_defaults()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Make sure data_dir exists
|
||||
try:
|
||||
os.makedirs(self.get("data_dir"), exist_ok=True)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
|
@ -191,7 +185,7 @@ class Settings(object):
|
|||
if key == "control_port_port" or key == "socks_port":
|
||||
try:
|
||||
val = int(val)
|
||||
except:
|
||||
except Exception:
|
||||
if key == "control_port_port":
|
||||
val = self.default_settings["control_port_port"]
|
||||
elif key == "socks_port":
|
||||
|
|
|
@ -26,6 +26,26 @@ from datetime import datetime
|
|||
from flask import Request, request, render_template, make_response, flash, redirect
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
# Receive mode uses a special flask requests object, ReceiveModeRequest, in
|
||||
# order to keep track of upload progress. Here's what happens when someone
|
||||
# uploads files:
|
||||
# - new ReceiveModeRequest object is created
|
||||
# - ReceiveModeRequest.__init__
|
||||
# - creates a directory based on the timestamp
|
||||
# - creates empty self.progress = dict, which will map uploaded files to their upload progress
|
||||
# - ReceiveModeRequest._get_file_stream
|
||||
# - called for each file that gets upload
|
||||
# - the first time, send REQUEST_STARTED to GUI, and append to self.web.receive_mode.uploads_in_progress
|
||||
# - updates self.progress[self.filename] for the current file
|
||||
# - uses custom ReceiveModeFile to save file to disk
|
||||
# - ReceiveModeRequest.file_write_func called on each write
|
||||
# - Display progress in CLI, and send REQUEST_PROGRESS to GUI
|
||||
# - ReceiveModeRequest.file_close_func called when each file closes
|
||||
# - self.progress[filename]["complete"] = True
|
||||
# - ReceiveModeRequest.close
|
||||
# - send either REQUEST_UPLOAD_CANCELED or REQUEST_UPLOAD_FINISHED to GUI
|
||||
# - remove from self.web.receive_mode.uploads_in_progress
|
||||
|
||||
|
||||
class ReceiveModeWeb:
|
||||
"""
|
||||
|
@ -66,6 +86,8 @@ class ReceiveModeWeb:
|
|||
render_template(
|
||||
"receive.html",
|
||||
static_url_path=self.web.static_url_path,
|
||||
disable_text=self.web.settings.get("receive", "disable_text"),
|
||||
disable_files=self.web.settings.get("receive", "disable_files"),
|
||||
title=self.web.settings.get("general", "title"),
|
||||
)
|
||||
)
|
||||
|
@ -77,44 +99,58 @@ class ReceiveModeWeb:
|
|||
Handle the upload files POST request, though at this point, the files have
|
||||
already been uploaded and saved to their correct locations.
|
||||
"""
|
||||
files = request.files.getlist("file[]")
|
||||
filenames = []
|
||||
for f in files:
|
||||
if f.filename != "":
|
||||
filename = secure_filename(f.filename)
|
||||
filenames.append(filename)
|
||||
local_path = os.path.join(request.receive_mode_dir, filename)
|
||||
basename = os.path.basename(local_path)
|
||||
message_received = request.includes_message
|
||||
|
||||
# Tell the GUI the receive mode directory for this file
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_UPLOAD_SET_DIR,
|
||||
request.path,
|
||||
{
|
||||
"id": request.history_id,
|
||||
"filename": basename,
|
||||
"dir": request.receive_mode_dir,
|
||||
},
|
||||
)
|
||||
files_received = 0
|
||||
if not self.web.settings.get("receive", "disable_files"):
|
||||
files = request.files.getlist("file[]")
|
||||
|
||||
self.common.log(
|
||||
"ReceiveModeWeb",
|
||||
"define_routes",
|
||||
f"/upload, uploaded {f.filename}, saving to {local_path}",
|
||||
)
|
||||
print(f"\nReceived: {local_path}")
|
||||
filenames = []
|
||||
for f in files:
|
||||
if f.filename != "":
|
||||
filename = secure_filename(f.filename)
|
||||
filenames.append(filename)
|
||||
local_path = os.path.join(request.receive_mode_dir, filename)
|
||||
basename = os.path.basename(local_path)
|
||||
|
||||
# Tell the GUI the receive mode directory for this file
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_UPLOAD_SET_DIR,
|
||||
request.path,
|
||||
{
|
||||
"id": request.history_id,
|
||||
"filename": basename,
|
||||
"dir": request.receive_mode_dir,
|
||||
},
|
||||
)
|
||||
|
||||
self.common.log(
|
||||
"ReceiveModeWeb",
|
||||
"define_routes",
|
||||
f"/upload, uploaded {f.filename}, saving to {local_path}",
|
||||
)
|
||||
print(f"Received: {local_path}")
|
||||
|
||||
files_received = len(filenames)
|
||||
|
||||
# Send webhook if configured
|
||||
if (
|
||||
self.web.settings.get("receive", "webhook_url")
|
||||
self.web.settings.get("receive", "webhook_url") is not None
|
||||
and not request.upload_error
|
||||
and len(files) > 0
|
||||
and (message_received or files_received)
|
||||
):
|
||||
if len(files) == 1:
|
||||
file_msg = "1 file"
|
||||
else:
|
||||
file_msg = f"{len(files)} files"
|
||||
self.send_webhook_notification(f"{file_msg} uploaded to OnionShare")
|
||||
msg = ""
|
||||
if files_received > 0:
|
||||
if files_received == 1:
|
||||
msg += "1 file"
|
||||
else:
|
||||
msg += f"{files_received} files"
|
||||
if message_received:
|
||||
if msg == "":
|
||||
msg = "A text message"
|
||||
else:
|
||||
msg += " and a text message"
|
||||
self.send_webhook_notification(f"{msg} submitted to OnionShare")
|
||||
|
||||
if request.upload_error:
|
||||
self.common.log(
|
||||
|
@ -142,21 +178,27 @@ class ReceiveModeWeb:
|
|||
if ajax:
|
||||
info_flashes = []
|
||||
|
||||
if len(filenames) == 0:
|
||||
msg = "No files uploaded"
|
||||
if ajax:
|
||||
info_flashes.append(msg)
|
||||
else:
|
||||
flash(msg, "info")
|
||||
else:
|
||||
msg = "Sent "
|
||||
if files_received > 0:
|
||||
files_msg = ""
|
||||
for filename in filenames:
|
||||
msg += f"{filename}, "
|
||||
msg = msg.rstrip(", ")
|
||||
if ajax:
|
||||
info_flashes.append(msg)
|
||||
files_msg += f"{filename}, "
|
||||
files_msg = files_msg.rstrip(", ")
|
||||
|
||||
if message_received:
|
||||
if files_received > 0:
|
||||
msg = f"Message submitted, uploaded {files_msg}"
|
||||
else:
|
||||
flash(msg, "info")
|
||||
msg = "Message submitted"
|
||||
else:
|
||||
if files_received > 0:
|
||||
msg = f"Uploaded {files_msg}"
|
||||
else:
|
||||
msg = "Nothing submitted"
|
||||
|
||||
if ajax:
|
||||
info_flashes.append(msg)
|
||||
else:
|
||||
flash(msg, "info")
|
||||
|
||||
if self.can_upload:
|
||||
if ajax:
|
||||
|
@ -238,7 +280,7 @@ class ReceiveModeFile(object):
|
|||
self.upload_error = False
|
||||
try:
|
||||
self.f = open(self.filename_in_progress, "wb+")
|
||||
except:
|
||||
except Exception:
|
||||
# This will only happen if someone is messing with the data dir while
|
||||
# OnionShare is running, but if it does make sure to throw an error
|
||||
self.upload_error = True
|
||||
|
@ -286,7 +328,7 @@ class ReceiveModeFile(object):
|
|||
bytes_written = self.f.write(b)
|
||||
self.onionshare_write_func(self.onionshare_filename, bytes_written)
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
self.upload_error = True
|
||||
|
||||
def close(self):
|
||||
|
@ -300,7 +342,7 @@ class ReceiveModeFile(object):
|
|||
# Rename the in progress file to the final filename
|
||||
os.rename(self.filename_in_progress, self.filename)
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
self.upload_error = True
|
||||
|
||||
self.onionshare_close_func(self.onionshare_filename, self.upload_error)
|
||||
|
@ -316,8 +358,7 @@ class ReceiveModeRequest(Request):
|
|||
super(ReceiveModeRequest, self).__init__(environ, populate_request, shallow)
|
||||
self.web = environ["web"]
|
||||
self.stop_q = environ["stop_q"]
|
||||
|
||||
self.web.common.log("ReceiveModeRequest", "__init__")
|
||||
self.filename = None
|
||||
|
||||
# Prevent running the close() method more than once
|
||||
self.closed = False
|
||||
|
@ -329,13 +370,15 @@ class ReceiveModeRequest(Request):
|
|||
self.upload_request = True
|
||||
|
||||
if self.upload_request:
|
||||
self.web.common.log("ReceiveModeRequest", "__init__")
|
||||
|
||||
# No errors yet
|
||||
self.upload_error = False
|
||||
|
||||
# Figure out what files should be saved
|
||||
now = datetime.now()
|
||||
date_dir = now.strftime("%Y-%m-%d")
|
||||
time_dir = now.strftime("%H.%M.%S")
|
||||
time_dir = now.strftime("%H%M%S")
|
||||
self.receive_mode_dir = os.path.join(
|
||||
self.web.settings.get("receive", "data_dir"), date_dir, time_dir
|
||||
)
|
||||
|
@ -383,6 +426,9 @@ class ReceiveModeRequest(Request):
|
|||
)
|
||||
self.upload_error = True
|
||||
|
||||
# Figure out the message filename, in case there is a message
|
||||
self.message_filename = f"{self.receive_mode_dir}-message.txt"
|
||||
|
||||
# If there's an error so far, finish early
|
||||
if self.upload_error:
|
||||
return
|
||||
|
@ -400,7 +446,7 @@ class ReceiveModeRequest(Request):
|
|||
# Figure out the content length
|
||||
try:
|
||||
self.content_length = int(self.headers["Content-Length"])
|
||||
except:
|
||||
except Exception:
|
||||
self.content_length = 0
|
||||
|
||||
date_str = datetime.now().strftime("%b %d, %I:%M%p")
|
||||
|
@ -412,6 +458,60 @@ class ReceiveModeRequest(Request):
|
|||
|
||||
self.previous_file = None
|
||||
|
||||
# Is there a text message?
|
||||
self.includes_message = False
|
||||
if not self.web.settings.get("receive", "disable_text"):
|
||||
text_message = self.form.get("text")
|
||||
if text_message:
|
||||
if text_message.strip() != "":
|
||||
self.includes_message = True
|
||||
|
||||
with open(self.message_filename, "w") as f:
|
||||
f.write(text_message)
|
||||
|
||||
self.web.common.log(
|
||||
"ReceiveModeRequest",
|
||||
"__init__",
|
||||
f"saved message to {self.message_filename}",
|
||||
)
|
||||
print(f"Received: {self.message_filename}")
|
||||
|
||||
# Tell the GUI about the message
|
||||
self.tell_gui_request_started()
|
||||
self.web.common.log(
|
||||
"ReceiveModeRequest",
|
||||
"__init__",
|
||||
"sending REQUEST_UPLOAD_INCLUDES_MESSAGE to GUI",
|
||||
)
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_UPLOAD_INCLUDES_MESSAGE,
|
||||
self.path,
|
||||
{
|
||||
"id": self.history_id,
|
||||
"filename": self.message_filename,
|
||||
},
|
||||
)
|
||||
|
||||
def tell_gui_request_started(self):
|
||||
# Tell the GUI about the request
|
||||
if not self.told_gui_about_request:
|
||||
self.web.common.log(
|
||||
"ReceiveModeRequest",
|
||||
"tell_gui_request_started",
|
||||
"sending REQUEST_STARTED to GUI",
|
||||
)
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_STARTED,
|
||||
self.path,
|
||||
{
|
||||
"id": self.history_id,
|
||||
"content_length": self.content_length,
|
||||
},
|
||||
)
|
||||
self.web.receive_mode.uploads_in_progress.append(self.history_id)
|
||||
|
||||
self.told_gui_about_request = True
|
||||
|
||||
def _get_file_stream(
|
||||
self, total_content_length, content_type, filename=None, content_length=None
|
||||
):
|
||||
|
@ -420,16 +520,7 @@ class ReceiveModeRequest(Request):
|
|||
writable stream.
|
||||
"""
|
||||
if self.upload_request:
|
||||
if not self.told_gui_about_request:
|
||||
# Tell the GUI about the request
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_STARTED,
|
||||
self.path,
|
||||
{"id": self.history_id, "content_length": self.content_length},
|
||||
)
|
||||
self.web.receive_mode.uploads_in_progress.append(self.history_id)
|
||||
|
||||
self.told_gui_about_request = True
|
||||
self.tell_gui_request_started()
|
||||
|
||||
self.filename = secure_filename(filename)
|
||||
|
||||
|
@ -456,29 +547,47 @@ class ReceiveModeRequest(Request):
|
|||
return
|
||||
self.closed = True
|
||||
|
||||
self.web.common.log("ReceiveModeRequest", "close")
|
||||
if self.upload_request:
|
||||
self.web.common.log("ReceiveModeRequest", "close")
|
||||
|
||||
try:
|
||||
if self.told_gui_about_request:
|
||||
history_id = self.history_id
|
||||
|
||||
if (
|
||||
not self.web.stop_q.empty()
|
||||
or not self.progress[self.filename]["complete"]
|
||||
if not self.web.stop_q.empty() or (
|
||||
self.filename in self.progress
|
||||
and not self.progress[self.filename]["complete"]
|
||||
):
|
||||
# Inform the GUI that the upload has canceled
|
||||
self.web.common.log(
|
||||
"ReceiveModeRequest",
|
||||
"close",
|
||||
"sending REQUEST_UPLOAD_CANCELED to GUI",
|
||||
)
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_UPLOAD_CANCELED, self.path, {"id": history_id}
|
||||
self.web.REQUEST_UPLOAD_CANCELED,
|
||||
self.path,
|
||||
{"id": history_id},
|
||||
)
|
||||
else:
|
||||
# Inform the GUI that the upload has finished
|
||||
self.web.common.log(
|
||||
"ReceiveModeRequest",
|
||||
"close",
|
||||
"sending REQUEST_UPLOAD_FINISHED to GUI",
|
||||
)
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_UPLOAD_FINISHED, self.path, {"id": history_id}
|
||||
self.web.REQUEST_UPLOAD_FINISHED,
|
||||
self.path,
|
||||
{"id": history_id},
|
||||
)
|
||||
self.web.receive_mode.uploads_in_progress.remove(history_id)
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
# If no files were written to self.receive_mode_dir, delete it
|
||||
try:
|
||||
if len(os.listdir(self.receive_mode_dir)) == 0:
|
||||
os.rmdir(self.receive_mode_dir)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def file_write_func(self, filename, length):
|
||||
"""
|
||||
|
@ -496,7 +605,11 @@ class ReceiveModeRequest(Request):
|
|||
size_str = self.web.common.human_readable_filesize(
|
||||
self.progress[filename]["uploaded_bytes"]
|
||||
)
|
||||
print(f"\r=> {size_str} {filename} ", end="")
|
||||
|
||||
if self.web.common.verbose:
|
||||
print(f"=> {size_str} {filename}")
|
||||
else:
|
||||
print(f"\r=> {size_str} {filename} ", end="")
|
||||
|
||||
# Update the GUI on the upload progress
|
||||
if self.told_gui_about_request:
|
||||
|
|
|
@ -55,6 +55,15 @@ class SendBaseModeWeb:
|
|||
self.define_routes()
|
||||
self.init()
|
||||
|
||||
def fix_windows_paths(self, path):
|
||||
"""
|
||||
If on Windows, replace backslashes with slashes
|
||||
"""
|
||||
if self.common.platform == "Windows":
|
||||
return path.replace("\\", "/")
|
||||
|
||||
return path
|
||||
|
||||
def set_file_info(self, filenames, processed_size_callback=None):
|
||||
"""
|
||||
Build a data structure that describes the list of files
|
||||
|
@ -70,40 +79,48 @@ class SendBaseModeWeb:
|
|||
self.root_files = (
|
||||
{}
|
||||
) # This is only the root files and dirs, as opposed to all of them
|
||||
self.cleanup_filenames = []
|
||||
self.cur_history_id = 0
|
||||
self.file_info = {"files": [], "dirs": []}
|
||||
self.gzip_individual_files = {}
|
||||
self.init()
|
||||
|
||||
# Windows paths use backslashes, but website paths use forward slashes. We have to
|
||||
# make sure we're stripping the correct type of slash
|
||||
if self.common.platform == "Windows":
|
||||
slash = "\\"
|
||||
else:
|
||||
slash = "/"
|
||||
|
||||
# Build the file list
|
||||
for filename in filenames:
|
||||
basename = os.path.basename(filename.rstrip("/"))
|
||||
basename = os.path.basename(filename.rstrip(slash))
|
||||
|
||||
# If it's a filename, add it
|
||||
if os.path.isfile(filename):
|
||||
self.files[basename] = filename
|
||||
self.root_files[basename] = filename
|
||||
self.files[self.fix_windows_paths(basename)] = filename
|
||||
self.root_files[self.fix_windows_paths(basename)] = filename
|
||||
|
||||
# If it's a directory, add it recursively
|
||||
elif os.path.isdir(filename):
|
||||
self.root_files[basename] = filename
|
||||
self.root_files[self.fix_windows_paths(basename)] = filename
|
||||
|
||||
for root, _, nested_filenames in os.walk(filename):
|
||||
# Normalize the root path. So if the directory name is "/home/user/Documents/some_folder",
|
||||
# and it has a nested folder foobar, the root is "/home/user/Documents/some_folder/foobar".
|
||||
# The normalized_root should be "some_folder/foobar"
|
||||
normalized_root = os.path.join(
|
||||
basename, root[len(filename) :].lstrip("/")
|
||||
).rstrip("/")
|
||||
basename, root[len(filename) :].lstrip(slash)
|
||||
).rstrip(slash)
|
||||
|
||||
# Add the dir itself
|
||||
self.files[normalized_root] = root
|
||||
self.files[self.fix_windows_paths(normalized_root)] = root
|
||||
|
||||
# Add the files in this dir
|
||||
for nested_filename in nested_filenames:
|
||||
self.files[
|
||||
os.path.join(normalized_root, nested_filename)
|
||||
self.fix_windows_paths(
|
||||
os.path.join(normalized_root, nested_filename)
|
||||
)
|
||||
] = os.path.join(root, nested_filename)
|
||||
|
||||
self.set_file_info_custom(filenames, processed_size_callback)
|
||||
|
@ -177,7 +194,7 @@ class SendBaseModeWeb:
|
|||
self.gzip_individual_files[filesystem_path] = gzip_filename
|
||||
|
||||
# Make sure the gzip file gets cleaned up when onionshare stops
|
||||
self.cleanup_filenames.append(gzip_filename)
|
||||
self.web.cleanup_filenames.append(gzip_filename)
|
||||
|
||||
file_to_download = self.gzip_individual_files[filesystem_path]
|
||||
filesize = os.path.getsize(self.gzip_individual_files[filesystem_path])
|
||||
|
@ -242,7 +259,7 @@ class SendBaseModeWeb:
|
|||
},
|
||||
)
|
||||
done = False
|
||||
except:
|
||||
except Exception:
|
||||
# Looks like the download was canceled
|
||||
done = True
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ def make_etag(data):
|
|||
else:
|
||||
break
|
||||
|
||||
hash_value = binascii.hexlify(hasher.digest()).decode('utf-8')
|
||||
hash_value = binascii.hexlify(hasher.digest()).decode("utf-8")
|
||||
return '"sha256:{}"'.format(hash_value)
|
||||
|
||||
|
||||
|
@ -53,13 +53,13 @@ def parse_range_header(range_header: str, target_size: int) -> list:
|
|||
if range_header is None:
|
||||
return [(0, end_index)]
|
||||
|
||||
bytes_ = 'bytes='
|
||||
bytes_ = "bytes="
|
||||
if not range_header.startswith(bytes_):
|
||||
abort(416)
|
||||
|
||||
ranges = []
|
||||
for range_ in range_header[len(bytes_):].split(','):
|
||||
split = range_.split('-')
|
||||
for range_ in range_header[len(bytes_) :].split(","):
|
||||
split = range_.split("-")
|
||||
if len(split) == 1:
|
||||
try:
|
||||
start = int(split[0])
|
||||
|
@ -194,13 +194,17 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
etag = self.download_etag
|
||||
|
||||
# for range requests
|
||||
range_, status_code = self.get_range_and_status_code(self.filesize, etag, self.last_modified)
|
||||
range_, status_code = self.get_range_and_status_code(
|
||||
self.filesize, etag, self.last_modified
|
||||
)
|
||||
|
||||
# Tell GUI the download started
|
||||
history_id = self.cur_history_id
|
||||
self.cur_history_id += 1
|
||||
self.web.add_request(
|
||||
self.web.REQUEST_STARTED, request_path, {"id": history_id, "use_gzip": use_gzip}
|
||||
self.web.REQUEST_STARTED,
|
||||
request_path,
|
||||
{"id": history_id, "use_gzip": use_gzip},
|
||||
)
|
||||
|
||||
basename = os.path.basename(self.download_filename)
|
||||
|
@ -209,32 +213,41 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
r = Response()
|
||||
else:
|
||||
r = Response(
|
||||
self.generate(shutdown_func, range_, file_to_download, request_path,
|
||||
history_id, self.filesize))
|
||||
self.generate(
|
||||
shutdown_func,
|
||||
range_,
|
||||
file_to_download,
|
||||
request_path,
|
||||
history_id,
|
||||
self.filesize,
|
||||
)
|
||||
)
|
||||
|
||||
if use_gzip:
|
||||
r.headers.set('Content-Encoding', 'gzip')
|
||||
r.headers.set("Content-Encoding", "gzip")
|
||||
|
||||
r.headers.set('Content-Length', range_[1] - range_[0] + 1)
|
||||
r.headers.set("Content-Length", range_[1] - range_[0] + 1)
|
||||
filename_dict = {
|
||||
"filename": unidecode(basename),
|
||||
"filename*": "UTF-8''%s" % url_quote(basename),
|
||||
}
|
||||
r.headers.set('Content-Disposition', 'attachment', **filename_dict)
|
||||
r.headers.set("Content-Disposition", "attachment", **filename_dict)
|
||||
r = self.web.add_security_headers(r)
|
||||
# guess content type
|
||||
(content_type, _) = mimetypes.guess_type(basename, strict=False)
|
||||
if content_type is not None:
|
||||
r.headers.set('Content-Type', content_type)
|
||||
r.headers.set('Accept-Ranges', 'bytes')
|
||||
r.headers.set('ETag', etag)
|
||||
r.headers.set('Last-Modified', http_date(self.last_modified))
|
||||
r.headers.set("Content-Type", content_type)
|
||||
r.headers.set("Accept-Ranges", "bytes")
|
||||
r.headers.set("ETag", etag)
|
||||
r.headers.set("Last-Modified", http_date(self.last_modified))
|
||||
# we need to set this for range requests
|
||||
r.headers.set('Vary', 'Accept-Encoding')
|
||||
r.headers.set("Vary", "Accept-Encoding")
|
||||
|
||||
if status_code == 206:
|
||||
r.headers.set('Content-Range',
|
||||
'bytes {}-{}/{}'.format(range_[0], range_[1], self.filesize))
|
||||
r.headers.set(
|
||||
"Content-Range",
|
||||
"bytes {}-{}/{}".format(range_[0], range_[1], self.filesize),
|
||||
)
|
||||
|
||||
r.status_code = status_code
|
||||
|
||||
|
@ -244,17 +257,19 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
def get_range_and_status_code(cls, dl_size, etag, last_modified):
|
||||
use_default_range = True
|
||||
status_code = 200
|
||||
range_header = request.headers.get('Range')
|
||||
range_header = request.headers.get("Range")
|
||||
|
||||
# range requests are only allowed for get
|
||||
if request.method == 'GET':
|
||||
if request.method == "GET":
|
||||
ranges = parse_range_header(range_header, dl_size)
|
||||
if not (len(ranges) == 1 and ranges[0][0] == 0 and ranges[0][1] == dl_size - 1):
|
||||
if not (
|
||||
len(ranges) == 1 and ranges[0][0] == 0 and ranges[0][1] == dl_size - 1
|
||||
):
|
||||
use_default_range = False
|
||||
status_code = 206
|
||||
|
||||
if range_header:
|
||||
if_range = request.headers.get('If-Range')
|
||||
if_range = request.headers.get("If-Range")
|
||||
if if_range and if_range != etag:
|
||||
use_default_range = True
|
||||
status_code = 200
|
||||
|
@ -266,11 +281,11 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
abort(416) # We don't support multipart range requests yet
|
||||
range_ = ranges[0]
|
||||
|
||||
etag_header = request.headers.get('ETag')
|
||||
etag_header = request.headers.get("ETag")
|
||||
if etag_header is not None and etag_header != etag:
|
||||
abort(412)
|
||||
|
||||
if_unmod = request.headers.get('If-Unmodified-Since')
|
||||
if_unmod = request.headers.get("If-Unmodified-Since")
|
||||
if if_unmod:
|
||||
if_date = parse_date(if_unmod)
|
||||
if if_date and if_date > last_modified:
|
||||
|
@ -280,10 +295,12 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
|
||||
return range_, status_code
|
||||
|
||||
def generate(self, shutdown_func, range_, file_to_download, path, history_id, filesize):
|
||||
def generate(
|
||||
self, shutdown_func, range_, file_to_download, path, history_id, filesize
|
||||
):
|
||||
# The user hasn't canceled the download
|
||||
self.client_cancel = False
|
||||
|
||||
|
||||
# Starting a new download
|
||||
if self.web.settings.get("share", "autostop_sharing"):
|
||||
self.download_in_progress = True
|
||||
|
@ -326,9 +343,7 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
):
|
||||
sys.stdout.write(
|
||||
"\r{0:s}, {1:.2f}% ".format(
|
||||
self.common.human_readable_filesize(
|
||||
downloaded_bytes
|
||||
),
|
||||
self.common.human_readable_filesize(downloaded_bytes),
|
||||
percent,
|
||||
)
|
||||
)
|
||||
|
@ -337,10 +352,14 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
self.web.add_request(
|
||||
self.web.REQUEST_PROGRESS,
|
||||
path,
|
||||
{"id": history_id, "bytes": downloaded_bytes, 'total_bytes': filesize,},
|
||||
{
|
||||
"id": history_id,
|
||||
"bytes": downloaded_bytes,
|
||||
"total_bytes": filesize,
|
||||
},
|
||||
)
|
||||
self.web.done = False
|
||||
except:
|
||||
except Exception:
|
||||
# looks like the download was canceled
|
||||
self.web.done = True
|
||||
canceled = True
|
||||
|
@ -367,10 +386,9 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
if shutdown_func is None:
|
||||
raise RuntimeError("Not running with the Werkzeug Server")
|
||||
shutdown_func()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def directory_listing_template(
|
||||
self, path, files, dirs, breadcrumbs, breadcrumbs_leaf
|
||||
):
|
||||
|
@ -466,7 +484,7 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
if len(self.file_info["files"]) == 1 and len(self.file_info["dirs"]) == 0:
|
||||
self.download_filename = self.file_info["files"][0]["filename"]
|
||||
self.download_filesize = self.file_info["files"][0]["size"]
|
||||
with open(self.download_filename, 'rb') as f:
|
||||
with open(self.download_filename, "rb") as f:
|
||||
self.download_etag = make_etag(f)
|
||||
|
||||
# Compress the file with gzip now, so we don't have to do it on each request
|
||||
|
@ -475,11 +493,11 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
self.download_filename, self.gzip_filename, 6, processed_size_callback
|
||||
)
|
||||
self.gzip_filesize = os.path.getsize(self.gzip_filename)
|
||||
with open(self.gzip_filename, 'rb') as f:
|
||||
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.cleanup_filenames.append(self.gzip_filename)
|
||||
self.web.cleanup_filenames.append(self.gzip_filename)
|
||||
|
||||
self.is_zipped = False
|
||||
|
||||
|
@ -502,11 +520,12 @@ class ShareModeWeb(SendBaseModeWeb):
|
|||
|
||||
self.zip_writer.close()
|
||||
self.download_filesize = os.path.getsize(self.download_filename)
|
||||
with open(self.download_filename, 'rb') as f:
|
||||
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.cleanup_filenames.append(self.zip_writer.zip_filename)
|
||||
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
|
||||
|
||||
|
@ -527,8 +546,9 @@ class ZipWriter(object):
|
|||
if zip_filename:
|
||||
self.zip_filename = zip_filename
|
||||
else:
|
||||
self.zip_temp_dir = tempfile.mkdtemp()
|
||||
self.zip_filename = (
|
||||
f"{tempfile.mkdtemp()}/onionshare_{self.common.random_string(4, 6)}.zip"
|
||||
f"{self.zip_temp_dir}/onionshare_{self.common.random_string(4, 6)}.zip"
|
||||
)
|
||||
|
||||
self.z = zipfile.ZipFile(self.zip_filename, "w", allowZip64=True)
|
||||
|
|
|
@ -21,6 +21,7 @@ import logging
|
|||
import os
|
||||
import queue
|
||||
import requests
|
||||
import shutil
|
||||
from distutils.version import LooseVersion as Version
|
||||
|
||||
import flask
|
||||
|
@ -41,6 +42,7 @@ from .receive_mode import ReceiveModeWeb, ReceiveModeWSGIMiddleware, ReceiveMode
|
|||
from .website_mode import WebsiteModeWeb
|
||||
from .chat_mode import ChatModeWeb
|
||||
|
||||
|
||||
# Stub out flask's show_server_banner function, to avoiding showing warnings that
|
||||
# are not applicable to OnionShare
|
||||
def stubbed_show_server_banner(env, debug, app_import_path, eager_loading):
|
||||
|
@ -49,7 +51,7 @@ def stubbed_show_server_banner(env, debug, app_import_path, eager_loading):
|
|||
|
||||
try:
|
||||
flask.cli.show_server_banner = stubbed_show_server_banner
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
@ -63,16 +65,17 @@ class Web:
|
|||
REQUEST_PROGRESS = 2
|
||||
REQUEST_CANCELED = 3
|
||||
REQUEST_RATE_LIMIT = 4
|
||||
REQUEST_UPLOAD_FILE_RENAMED = 5
|
||||
REQUEST_UPLOAD_SET_DIR = 6
|
||||
REQUEST_UPLOAD_FINISHED = 7
|
||||
REQUEST_UPLOAD_CANCELED = 8
|
||||
REQUEST_INDIVIDUAL_FILE_STARTED = 9
|
||||
REQUEST_INDIVIDUAL_FILE_PROGRESS = 10
|
||||
REQUEST_INDIVIDUAL_FILE_CANCELED = 11
|
||||
REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 12
|
||||
REQUEST_OTHER = 13
|
||||
REQUEST_INVALID_PASSWORD = 14
|
||||
REQUEST_UPLOAD_INCLUDES_MESSAGE = 5
|
||||
REQUEST_UPLOAD_FILE_RENAMED = 6
|
||||
REQUEST_UPLOAD_SET_DIR = 7
|
||||
REQUEST_UPLOAD_FINISHED = 8
|
||||
REQUEST_UPLOAD_CANCELED = 9
|
||||
REQUEST_INDIVIDUAL_FILE_STARTED = 10
|
||||
REQUEST_INDIVIDUAL_FILE_PROGRESS = 11
|
||||
REQUEST_INDIVIDUAL_FILE_CANCELED = 12
|
||||
REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 13
|
||||
REQUEST_OTHER = 14
|
||||
REQUEST_INVALID_PASSWORD = 15
|
||||
|
||||
def __init__(self, common, is_gui, mode_settings, mode="share"):
|
||||
self.common = common
|
||||
|
@ -160,6 +163,8 @@ class Web:
|
|||
self.socketio.init_app(self.app)
|
||||
self.chat_mode = ChatModeWeb(self.common, self)
|
||||
|
||||
self.cleanup_filenames = []
|
||||
|
||||
def get_mode(self):
|
||||
if self.mode == "share":
|
||||
return self.share_mode
|
||||
|
@ -327,7 +332,7 @@ class Web:
|
|||
|
||||
def generate_password(self, saved_password=None):
|
||||
self.common.log("Web", "generate_password", f"saved_password={saved_password}")
|
||||
if saved_password != None and saved_password != "":
|
||||
if saved_password is not None and saved_password != "":
|
||||
self.password = saved_password
|
||||
self.common.log(
|
||||
"Web",
|
||||
|
@ -363,7 +368,7 @@ class Web:
|
|||
if func is None and self.mode != "chat":
|
||||
raise RuntimeError("Not running with the Werkzeug Server")
|
||||
func()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.running = False
|
||||
|
@ -421,3 +426,21 @@ class Web:
|
|||
|
||||
# Reset any password that was in use
|
||||
self.password = None
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Shut everything down and clean up temporary files, etc.
|
||||
"""
|
||||
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 = []
|
||||
|
|
57
cli/poetry.lock
generated
57
cli/poetry.lock
generated
|
@ -72,9 +72,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||
version = "7.1.2"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Cross-platform colored terminal text."
|
||||
marker = "sys_platform == \"win32\""
|
||||
name = "colorama"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
@ -98,7 +97,7 @@ description = "Highly concurrent networking library"
|
|||
name = "eventlet"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.30.2"
|
||||
version = "0.31.0"
|
||||
|
||||
[package.dependencies]
|
||||
dnspython = ">=1.15.0,<2.0.0"
|
||||
|
@ -130,7 +129,7 @@ description = "Basic and Digest HTTP authentication for Flask routes"
|
|||
name = "flask-httpauth"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "4.2.0"
|
||||
version = "4.3.0"
|
||||
|
||||
[package.dependencies]
|
||||
Flask = "*"
|
||||
|
@ -173,7 +172,7 @@ marker = "python_version < \"3.8\""
|
|||
name = "importlib-metadata"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "3.10.0"
|
||||
version = "4.0.1"
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=0.5"
|
||||
|
@ -316,7 +315,7 @@ description = "pytest: simple powerful testing with Python"
|
|||
name = "pytest"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "6.2.3"
|
||||
version = "6.2.4"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = ">=1.0"
|
||||
|
@ -341,7 +340,7 @@ description = "Engine.IO server"
|
|||
name = "python-engineio"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "4.0.1"
|
||||
version = "4.1.0"
|
||||
|
||||
[package.extras]
|
||||
asyncio_client = ["aiohttp (>=3.4)"]
|
||||
|
@ -353,11 +352,11 @@ description = "Socket.IO server"
|
|||
name = "python-socketio"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "5.1.0"
|
||||
version = "5.2.1"
|
||||
|
||||
[package.dependencies]
|
||||
bidict = ">=0.21.0"
|
||||
python-engineio = ">=4"
|
||||
python-engineio = ">=4.1.0"
|
||||
|
||||
[package.extras]
|
||||
asyncio_client = ["aiohttp (>=3.4)", "websockets (>=7.0)"]
|
||||
|
@ -391,7 +390,7 @@ description = "Python 2 and 3 compatibility utilities"
|
|||
name = "six"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
|
@ -416,7 +415,7 @@ marker = "python_version < \"3.8\""
|
|||
name = "typing-extensions"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.7.4.3"
|
||||
version = "3.10.0.0"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
|
@ -465,7 +464,7 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
|||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "8c04afd6b4605961ef8da2340c99f1d3dabc790bca8b57c1bdfb3e29130dc94d"
|
||||
content-hash = "a3456f7b6194c08460a78447e1397f319689aef5bc33d7bcb7622a495f947f87"
|
||||
python-versions = "^3.6"
|
||||
|
||||
[metadata.files]
|
||||
|
@ -541,16 +540,16 @@ dnspython = [
|
|||
{file = "dnspython-1.16.0.zip", hash = "sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01"},
|
||||
]
|
||||
eventlet = [
|
||||
{file = "eventlet-0.30.2-py2.py3-none-any.whl", hash = "sha256:89cc6dbfef47c4629cefead5fde21c5f2b33464d57f7df5fc5148f8b4de3fbb5"},
|
||||
{file = "eventlet-0.30.2.tar.gz", hash = "sha256:1811b122d9a45eb5bafba092d36911bca825f835cb648a862bbf984030acff9d"},
|
||||
{file = "eventlet-0.31.0-py2.py3-none-any.whl", hash = "sha256:27ae41fad9deed9bbf4166f3e3b65acc15d524d42210a518e5877da85a6b8c5d"},
|
||||
{file = "eventlet-0.31.0.tar.gz", hash = "sha256:b36ec2ecc003de87fc87b93197d77fea528aa0f9204a34fdf3b2f8d0f01e017b"},
|
||||
]
|
||||
flask = [
|
||||
{file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"},
|
||||
{file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"},
|
||||
]
|
||||
flask-httpauth = [
|
||||
{file = "Flask-HTTPAuth-4.2.0.tar.gz", hash = "sha256:8c7e49e53ce7dc14e66fe39b9334e4b7ceb8d0b99a6ba1c3562bb528ef9da84a"},
|
||||
{file = "Flask_HTTPAuth-4.2.0-py2.py3-none-any.whl", hash = "sha256:3fcedb99a03985915335a38c35bfee6765cbd66d7f46440fa3b42ae94a90fac7"},
|
||||
{file = "Flask-HTTPAuth-4.3.0.tar.gz", hash = "sha256:2e604283cc436f2fe59206500aef898427c76016d11e4924cd2c3ec827ab4116"},
|
||||
{file = "Flask_HTTPAuth-4.3.0-py2.py3-none-any.whl", hash = "sha256:ff3c08954993b3c415efa85a4993fe5d61f3e02e8f3e4e994b33f1ec2951baa9"},
|
||||
]
|
||||
flask-socketio = [
|
||||
{file = "Flask-SocketIO-5.0.1.tar.gz", hash = "sha256:5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227"},
|
||||
|
@ -606,8 +605,8 @@ idna = [
|
|||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-3.10.0-py3-none-any.whl", hash = "sha256:d2d46ef77ffc85cbf7dac7e81dd663fde71c45326131bea8033b9bad42268ebe"},
|
||||
{file = "importlib_metadata-3.10.0.tar.gz", hash = "sha256:c9db46394197244adf2f0b08ec5bc3cf16757e9590b02af1fca085c16c0d600a"},
|
||||
{file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"},
|
||||
{file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
|
@ -732,24 +731,24 @@ pysocks = [
|
|||
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"},
|
||||
{file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"},
|
||||
{file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"},
|
||||
{file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"},
|
||||
]
|
||||
python-engineio = [
|
||||
{file = "python-engineio-4.0.1.tar.gz", hash = "sha256:bb575c1a3512b4b5d4706f3071d5cc36e592459e99a47d9a4b7faabeba941377"},
|
||||
{file = "python_engineio-4.0.1-py2.py3-none-any.whl", hash = "sha256:759ec99d91b3d36b29596124c77ba7c063c7ab97685d318fb23a166d9a4b9187"},
|
||||
{file = "python-engineio-4.1.0.tar.gz", hash = "sha256:21e1bcc62f5573a4bb1c805e69915c5a4c5aa953005dde6c2f707c24554c1020"},
|
||||
{file = "python_engineio-4.1.0-py2.py3-none-any.whl", hash = "sha256:c0216b0f9584d6dd632dfd4e2b624d2cf5e97a55443a467f52b928b7e3e08998"},
|
||||
]
|
||||
python-socketio = [
|
||||
{file = "python-socketio-5.1.0.tar.gz", hash = "sha256:338cc29abb6f3ca14c88f1f8d05ed27c690df4648f62062b299f92625bbf7093"},
|
||||
{file = "python_socketio-5.1.0-py2.py3-none-any.whl", hash = "sha256:8a7ed43bfdbbb266eb8a661a0c9648dc94bcd9689566ae3ee08bf98eca8987af"},
|
||||
{file = "python-socketio-5.2.1.tar.gz", hash = "sha256:356a8a480fa316295b439d63a5f35a7a59fe65cee1ae35dee28e87d00e5aead6"},
|
||||
{file = "python_socketio-5.2.1-py2.py3-none-any.whl", hash = "sha256:8db0653e49389e609aa1048bdbc49da9b8d8ecdcf54e3c1e4c9cc37402cfdaef"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
||||
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
|
||||
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
stem = [
|
||||
{file = "stem-1.8.0.tar.gz", hash = "sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2"},
|
||||
|
@ -759,9 +758,9 @@ toml = [
|
|||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
|
||||
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
|
||||
{file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
|
||||
{file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
|
||||
{file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
|
||||
{file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
|
||||
]
|
||||
unidecode = [
|
||||
{file = "Unidecode-1.2.0-py2.py3-none-any.whl", hash = "sha256:12435ef2fc4cdfd9cf1035a1db7e98b6b047fe591892e81f34e94959591fad00"},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "onionshare_cli"
|
||||
version = "2.3.1"
|
||||
version = "2.3.2"
|
||||
description = "OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service."
|
||||
authors = ["Micah Lee <micah@micahflee.com>"]
|
||||
license = "GPLv3+"
|
||||
|
@ -30,6 +30,7 @@ urllib3 = "*"
|
|||
eventlet = "*"
|
||||
setuptools = "*"
|
||||
pynacl = "^1.4.0"
|
||||
colorama = "^0.4.4"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "*"
|
||||
|
|
|
@ -20,12 +20,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
"""
|
||||
import setuptools
|
||||
|
||||
version = "2.3.1"
|
||||
version = "2.3.2"
|
||||
|
||||
setuptools.setup(
|
||||
name="onionshare-cli",
|
||||
version=version,
|
||||
description="OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service.",
|
||||
description=(
|
||||
"OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, "
|
||||
"making it accessible as a Tor onion service, and generating an unguessable web address so others can "
|
||||
"download files from you, or upload files to you. It does _not_ require setting up a separate server or "
|
||||
"using a third party file-sharing service."
|
||||
),
|
||||
author="Micah Lee",
|
||||
author_email="micah@micahflee.com",
|
||||
maintainer="Micah Lee",
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
import sys
|
||||
|
||||
# Force tests to look for resources in the source code tree
|
||||
sys.onionshare_dev_mode = True
|
||||
|
||||
# Let OnionShare know the tests are running, to avoid colliding with settings files
|
||||
sys.onionshare_test_mode = True
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
@ -14,6 +7,11 @@ import pytest
|
|||
|
||||
from onionshare_cli import common, web
|
||||
|
||||
# Force tests to look for resources in the source code tree
|
||||
sys.onionshare_dev_mode = True
|
||||
|
||||
# Let OnionShare know the tests are running, to avoid colliding with settings files
|
||||
sys.onionshare_test_mode = True
|
||||
|
||||
# The temporary directory for CLI tests
|
||||
test_temp_dir = None
|
||||
|
@ -45,7 +43,7 @@ def temp_dir():
|
|||
|
||||
@pytest.fixture
|
||||
def temp_dir_1024(temp_dir):
|
||||
""" Create a temporary directory that has a single file of a
|
||||
"""Create a temporary directory that has a single file of a
|
||||
particular size (1024 bytes).
|
||||
"""
|
||||
|
||||
|
@ -56,10 +54,9 @@ def temp_dir_1024(temp_dir):
|
|||
return new_temp_dir
|
||||
|
||||
|
||||
# pytest > 2.9 only needs @pytest.fixture
|
||||
@pytest.yield_fixture
|
||||
@pytest.fixture
|
||||
def temp_dir_1024_delete(temp_dir):
|
||||
""" Create a temporary directory that has a single file of a
|
||||
"""Create a temporary directory that has a single file of a
|
||||
particular size (1024 bytes). The temporary directory (including
|
||||
the file inside) will be deleted after fixture usage.
|
||||
"""
|
||||
|
@ -73,15 +70,14 @@ def temp_dir_1024_delete(temp_dir):
|
|||
|
||||
@pytest.fixture
|
||||
def temp_file_1024(temp_dir):
|
||||
""" Create a temporary file of a particular size (1024 bytes). """
|
||||
"""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
|
||||
|
||||
|
||||
# pytest > 2.9 only needs @pytest.fixture
|
||||
@pytest.yield_fixture
|
||||
@pytest.fixture
|
||||
def temp_file_1024_delete(temp_dir):
|
||||
"""
|
||||
Create a temporary file of a particular size (1024 bytes).
|
||||
|
@ -95,8 +91,7 @@ def temp_file_1024_delete(temp_dir):
|
|||
yield tmp_file.name
|
||||
|
||||
|
||||
# pytest > 2.9 only needs @pytest.fixture
|
||||
@pytest.yield_fixture(scope="session")
|
||||
@pytest.fixture(scope="session")
|
||||
def custom_zw():
|
||||
zw = web.share_mode.ZipWriter(
|
||||
common.Common(),
|
||||
|
@ -108,8 +103,7 @@ def custom_zw():
|
|||
os.remove(zw.zip_filename)
|
||||
|
||||
|
||||
# pytest > 2.9 only needs @pytest.fixture
|
||||
@pytest.yield_fixture(scope="session")
|
||||
@pytest.fixture(scope="session")
|
||||
def default_zw():
|
||||
zw = web.share_mode.ZipWriter(common.Common())
|
||||
yield zw
|
||||
|
@ -117,7 +111,7 @@ def default_zw():
|
|||
tmp_dir = os.path.dirname(zw.zip_filename)
|
||||
try:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
@ -189,10 +183,3 @@ def time_strftime(monkeypatch):
|
|||
@pytest.fixture
|
||||
def common_obj():
|
||||
return common.Common()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settings_obj(sys_onionshare_dev_mode, platform_linux):
|
||||
_common = common.Common()
|
||||
_common.version = "DUMMY_VERSION_1.2.3"
|
||||
return settings.Settings(_common)
|
||||
|
|
|
@ -36,7 +36,6 @@ class TestOnionShare:
|
|||
def test_init(self, onionshare_obj):
|
||||
assert onionshare_obj.hidserv_dir is None
|
||||
assert onionshare_obj.onion_host is None
|
||||
assert onionshare_obj.cleanup_filenames == []
|
||||
assert onionshare_obj.local_only is False
|
||||
|
||||
def test_start_onion_service(self, onionshare_obj, mode_settings_obj):
|
||||
|
@ -48,11 +47,3 @@ class TestOnionShare:
|
|||
onionshare_obj.local_only = True
|
||||
onionshare_obj.start_onion_service("share", mode_settings_obj)
|
||||
assert onionshare_obj.onion_host == "127.0.0.1:{}".format(onionshare_obj.port)
|
||||
|
||||
def test_cleanup(self, onionshare_obj, temp_dir_1024, temp_file_1024):
|
||||
onionshare_obj.cleanup_filenames = [temp_dir_1024, temp_file_1024]
|
||||
onionshare_obj.cleanup()
|
||||
|
||||
assert os.path.exists(temp_dir_1024) is False
|
||||
assert os.path.exists(temp_dir_1024) is False
|
||||
assert onionshare_obj.cleanup_filenames == []
|
||||
|
|
|
@ -131,7 +131,7 @@ class TestGetAvailablePort:
|
|||
((random.randint(1024, 1500), random.randint(1800, 2048)) for _ in range(50)),
|
||||
)
|
||||
def test_returns_an_open_port(self, common_obj, port_min, port_max):
|
||||
""" get_available_port() should return an open port within the range """
|
||||
"""get_available_port() should return an open port within the range"""
|
||||
|
||||
port = common_obj.get_available_port(port_min, port_max)
|
||||
assert port_min <= port <= port_max
|
||||
|
@ -191,7 +191,9 @@ class TestGetTorPaths:
|
|||
@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows")
|
||||
def test_get_tor_paths_windows(self, platform_windows, common_obj, sys_frozen):
|
||||
base_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(common_obj.get_resource_path(""))), "tor"
|
||||
os.path.dirname(os.path.dirname(common_obj.get_resource_path(""))),
|
||||
"resources",
|
||||
"tor",
|
||||
)
|
||||
tor_path = os.path.join(os.path.join(base_path, "Tor"), "tor.exe")
|
||||
obfs4proxy_file_path = os.path.join(
|
||||
|
|
|
@ -7,6 +7,8 @@ import time
|
|||
import zipfile
|
||||
import tempfile
|
||||
import base64
|
||||
import shutil
|
||||
import sys
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
|
@ -42,13 +44,14 @@ RANDOM_STR_REGEX = re.compile(r"^[a-z2-7]+$")
|
|||
|
||||
|
||||
def web_obj(temp_dir, common_obj, mode, num_files=0):
|
||||
""" Creates a Web object, in either share mode or receive mode, ready for testing """
|
||||
"""Creates a Web object, in either share mode or receive mode, ready for testing"""
|
||||
common_obj.settings = Settings(common_obj)
|
||||
mode_settings = ModeSettings(common_obj)
|
||||
web = Web(common_obj, False, mode_settings, mode)
|
||||
web.generate_password()
|
||||
web.running = True
|
||||
|
||||
web.cleanup_filenames == []
|
||||
web.app.testing = True
|
||||
|
||||
# Share mode
|
||||
|
@ -100,7 +103,7 @@ class TestWeb:
|
|||
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||
web.settings.set("share", "autostop_sharing", True)
|
||||
|
||||
assert web.running == True
|
||||
assert web.running is True
|
||||
|
||||
with web.app.test_client() as c:
|
||||
# Download the first time
|
||||
|
@ -112,7 +115,7 @@ class TestWeb:
|
|||
or res.mimetype == "application/x-zip-compressed"
|
||||
)
|
||||
|
||||
assert web.running == False
|
||||
assert web.running is False
|
||||
|
||||
def test_share_mode_autostop_sharing_off(
|
||||
self, temp_dir, common_obj, temp_file_1024
|
||||
|
@ -120,7 +123,7 @@ class TestWeb:
|
|||
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||
web.settings.set("share", "autostop_sharing", False)
|
||||
|
||||
assert web.running == True
|
||||
assert web.running is True
|
||||
|
||||
with web.app.test_client() as c:
|
||||
# Download the first time
|
||||
|
@ -131,7 +134,7 @@ class TestWeb:
|
|||
res.mimetype == "application/zip"
|
||||
or res.mimetype == "application/x-zip-compressed"
|
||||
)
|
||||
assert web.running == True
|
||||
assert web.running is True
|
||||
|
||||
def test_receive_mode(self, temp_dir, common_obj):
|
||||
web = web_obj(temp_dir, common_obj, "receive")
|
||||
|
@ -183,7 +186,135 @@ class TestWeb:
|
|||
assert res.status_code == 200
|
||||
|
||||
assert webhook_url == "http://127.0.0.1:1337/example"
|
||||
assert webhook_data == "1 file uploaded to OnionShare"
|
||||
assert webhook_data == "1 file submitted to OnionShare"
|
||||
|
||||
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")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
web.settings.set("receive", "data_dir", data_dir)
|
||||
|
||||
with web.app.test_client() as c:
|
||||
res = c.post(
|
||||
"/upload-ajax",
|
||||
buffered=True,
|
||||
content_type="multipart/form-data",
|
||||
data={"text": "you know just sending an anonymous message"},
|
||||
headers=self._make_auth_headers(web.password),
|
||||
)
|
||||
content = res.get_data()
|
||||
assert res.status_code == 200
|
||||
assert b"Message submitted" in content
|
||||
|
||||
# ~/OnionShare should have a folder for the date
|
||||
filenames = os.listdir(data_dir)
|
||||
assert len(filenames) == 1
|
||||
data_dir_date = os.path.join(data_dir, filenames[0])
|
||||
|
||||
# The date folder should have a single message txt file, no folders
|
||||
filenames = os.listdir(data_dir_date)
|
||||
assert len(filenames) == 1
|
||||
assert filenames[0].endswith("-message.txt")
|
||||
|
||||
shutil.rmtree(data_dir)
|
||||
|
||||
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")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
web.settings.set("receive", "data_dir", data_dir)
|
||||
|
||||
with web.app.test_client() as c:
|
||||
res = c.post(
|
||||
"/upload-ajax",
|
||||
buffered=True,
|
||||
content_type="multipart/form-data",
|
||||
data={
|
||||
"file[]": (BytesIO(b"THIS IS A TEST FILE"), "new_york.jpg"),
|
||||
"text": "you know just sending an anonymous message",
|
||||
},
|
||||
headers=self._make_auth_headers(web.password),
|
||||
)
|
||||
content = res.get_data()
|
||||
assert res.status_code == 200
|
||||
assert b"Message submitted, uploaded new_york.jpg" in content
|
||||
|
||||
# Date folder should have a time folder with new_york.jpg, and a text message file
|
||||
data_dir_date = os.path.join(data_dir, os.listdir(data_dir)[0])
|
||||
filenames = os.listdir(data_dir_date)
|
||||
assert len(filenames) == 2
|
||||
time_str = filenames[0][0:6]
|
||||
assert time_str in filenames
|
||||
assert f"{time_str}-message.txt" in filenames
|
||||
data_dir_time = os.path.join(data_dir_date, time_str)
|
||||
assert os.path.isdir(data_dir_time)
|
||||
assert os.path.exists(os.path.join(data_dir_time, "new_york.jpg"))
|
||||
|
||||
shutil.rmtree(data_dir)
|
||||
|
||||
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")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
web.settings.set("receive", "data_dir", data_dir)
|
||||
|
||||
with web.app.test_client() as c:
|
||||
res = c.post(
|
||||
"/upload-ajax",
|
||||
buffered=True,
|
||||
content_type="multipart/form-data",
|
||||
data={"file[]": (BytesIO(b"THIS IS A TEST FILE"), "new_york.jpg")},
|
||||
headers=self._make_auth_headers(web.password),
|
||||
)
|
||||
content = res.get_data()
|
||||
assert res.status_code == 200
|
||||
assert b"Uploaded new_york.jpg" in content
|
||||
|
||||
# Date folder should have just a time folder with new_york.jpg
|
||||
data_dir_date = os.path.join(data_dir, os.listdir(data_dir)[0])
|
||||
filenames = os.listdir(data_dir_date)
|
||||
assert len(filenames) == 1
|
||||
time_str = filenames[0][0:6]
|
||||
assert time_str in filenames
|
||||
assert f"{time_str}-message.txt" not in filenames
|
||||
data_dir_time = os.path.join(data_dir_date, time_str)
|
||||
assert os.path.isdir(data_dir_time)
|
||||
assert os.path.exists(os.path.join(data_dir_time, "new_york.jpg"))
|
||||
|
||||
shutil.rmtree(data_dir)
|
||||
|
||||
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")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
web.settings.set("receive", "data_dir", data_dir)
|
||||
|
||||
with web.app.test_client() as c:
|
||||
res = c.post(
|
||||
"/upload-ajax",
|
||||
buffered=True,
|
||||
content_type="multipart/form-data",
|
||||
data={},
|
||||
headers=self._make_auth_headers(web.password),
|
||||
)
|
||||
content = res.get_data()
|
||||
assert res.status_code == 200
|
||||
assert b"Nothing submitted" in content
|
||||
|
||||
# Date folder should be empty
|
||||
data_dir_date = os.path.join(data_dir, os.listdir(data_dir)[0])
|
||||
filenames = os.listdir(data_dir_date)
|
||||
assert len(filenames) == 0
|
||||
|
||||
shutil.rmtree(data_dir)
|
||||
|
||||
def test_public_mode_on(self, temp_dir, common_obj):
|
||||
web = web_obj(temp_dir, common_obj, "receive")
|
||||
|
@ -192,7 +323,7 @@ class TestWeb:
|
|||
with web.app.test_client() as c:
|
||||
# Loading / should work without auth
|
||||
res = c.get("/")
|
||||
data1 = res.get_data()
|
||||
res.get_data()
|
||||
assert res.status_code == 200
|
||||
|
||||
def test_public_mode_off(self, temp_dir, common_obj):
|
||||
|
@ -215,6 +346,16 @@ class TestWeb:
|
|||
res.get_data()
|
||||
assert res.status_code == 200
|
||||
|
||||
def test_cleanup(self, common_obj, temp_dir_1024, temp_file_1024):
|
||||
web = web_obj(temp_dir_1024, common_obj, "share", 3)
|
||||
|
||||
web.cleanup_filenames = [temp_dir_1024, temp_file_1024]
|
||||
web.cleanup()
|
||||
|
||||
assert os.path.exists(temp_file_1024) is False
|
||||
assert os.path.exists(temp_dir_1024) is False
|
||||
assert web.cleanup_filenames == []
|
||||
|
||||
def _make_auth_headers(self, password):
|
||||
auth = base64.b64encode(b"onionshare:" + password.encode()).decode()
|
||||
h = Headers()
|
||||
|
@ -486,6 +627,7 @@ class TestRangeRequests:
|
|||
h.add("Authorization", "Basic " + auth)
|
||||
return h
|
||||
|
||||
@pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux")
|
||||
@check_unsupported("curl", ["--version"])
|
||||
def test_curl(self, temp_dir, tmpdir, common_obj):
|
||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||
|
@ -510,6 +652,7 @@ class TestRangeRequests:
|
|||
]
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux")
|
||||
@check_unsupported("wget", ["--version"])
|
||||
def test_wget(self, temp_dir, tmpdir, common_obj):
|
||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||
|
@ -533,6 +676,7 @@ class TestRangeRequests:
|
|||
]
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux")
|
||||
@check_unsupported("http", ["--version"])
|
||||
def test_httpie(self, temp_dir, common_obj):
|
||||
web = web_obj(temp_dir, common_obj, "share", 3)
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import pytest
|
||||
import subprocess
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
from werkzeug.exceptions import RequestedRangeNotSatisfiable
|
||||
|
||||
from onionshare_cli.web.share_mode import parse_range_header
|
||||
|
@ -9,24 +6,24 @@ from onionshare_cli.web.share_mode import parse_range_header
|
|||
|
||||
VALID_RANGES = [
|
||||
(None, 500, [(0, 499)]),
|
||||
('bytes=0', 500, [(0, 499)]),
|
||||
('bytes=100', 500, [(100, 499)]),
|
||||
('bytes=100-', 500, [(100, 499)]), # not in the RFC, but how curl sends
|
||||
('bytes=0-99', 500, [(0, 99)]),
|
||||
('bytes=0-599', 500, [(0, 499)]),
|
||||
('bytes=0-0', 500, [(0, 0)]),
|
||||
('bytes=-100', 500, [(400, 499)]),
|
||||
('bytes=0-99,100-199', 500, [(0, 199)]),
|
||||
('bytes=0-100,100-199', 500, [(0, 199)]),
|
||||
('bytes=0-99,101-199', 500, [(0, 99), (101, 199)]),
|
||||
('bytes=0-199,100-299', 500, [(0, 299)]),
|
||||
('bytes=0-99,200-299', 500, [(0, 99), (200, 299)]),
|
||||
("bytes=0", 500, [(0, 499)]),
|
||||
("bytes=100", 500, [(100, 499)]),
|
||||
("bytes=100-", 500, [(100, 499)]), # not in the RFC, but how curl sends
|
||||
("bytes=0-99", 500, [(0, 99)]),
|
||||
("bytes=0-599", 500, [(0, 499)]),
|
||||
("bytes=0-0", 500, [(0, 0)]),
|
||||
("bytes=-100", 500, [(400, 499)]),
|
||||
("bytes=0-99,100-199", 500, [(0, 199)]),
|
||||
("bytes=0-100,100-199", 500, [(0, 199)]),
|
||||
("bytes=0-99,101-199", 500, [(0, 99), (101, 199)]),
|
||||
("bytes=0-199,100-299", 500, [(0, 299)]),
|
||||
("bytes=0-99,200-299", 500, [(0, 99), (200, 299)]),
|
||||
]
|
||||
|
||||
|
||||
INVALID_RANGES = [
|
||||
'bytes=200-100',
|
||||
'bytes=0-100,300-200',
|
||||
"bytes=200-100",
|
||||
"bytes=0-100,300-200",
|
||||
]
|
||||
|
||||
|
||||
|
@ -38,4 +35,4 @@ def test_parse_ranges():
|
|||
|
||||
for invalid in INVALID_RANGES:
|
||||
with pytest.raises(RequestedRangeNotSatisfiable):
|
||||
parse_range_header(invalid, 500)
|
||||
parse_range_header(invalid, 500)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue