mirror of
https://github.com/9001/copyparty.git
synced 2025-11-05 06:13:20 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c03c65e07 | ||
|
|
d8ed006b9b | ||
|
|
63c0623a5e | ||
|
|
fd84506db0 | ||
|
|
d8bcb44e44 | ||
|
|
56a26b0916 | ||
|
|
efcf1d6b90 | ||
|
|
9f578bfec6 | ||
|
|
1f170d7d28 | ||
|
|
5ae14cf9be | ||
|
|
aaf9d53be9 | ||
|
|
75c73f7ba7 | ||
|
|
b6dba8beee | ||
|
|
94521cdc1a | ||
|
|
3365b1c355 | ||
|
|
6c957c4923 | ||
|
|
833997f04c | ||
|
|
68d51e4037 | ||
|
|
ce274d2011 | ||
|
|
280778ed43 | ||
|
|
0f558ecbbf |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -20,6 +20,13 @@
|
|||||||
"srv::r:aed:cnodupe"
|
"srv::r:aed:cnodupe"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "No debug",
|
||||||
|
"preLaunchTask": "no_dbg",
|
||||||
|
"type": "python",
|
||||||
|
//"request": "attach", "port": 42069
|
||||||
|
// fork: nc -l 42069 </dev/null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Run active unit test",
|
"name": "Run active unit test",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
|
|||||||
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@@ -50,11 +50,9 @@
|
|||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.makefile": "makefile"
|
"*.makefile": "makefile"
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSaveTimeout": 9001,
|
"python.formatting.blackArgs": [
|
||||||
"editor.formatOnSaveTimeout": 9001,
|
"-t",
|
||||||
//
|
"py27"
|
||||||
// things you may wanna edit:
|
],
|
||||||
//
|
"python.linting.enabled": true,
|
||||||
"python.pythonPath": "/usr/bin/python3",
|
|
||||||
//"python.linting.enabled": true,
|
|
||||||
}
|
}
|
||||||
5
.vscode/tasks.json
vendored
5
.vscode/tasks.json
vendored
@@ -5,6 +5,11 @@
|
|||||||
"label": "pre",
|
"label": "pre",
|
||||||
"command": "true;rm -rf inc/* inc/.hist/;mkdir -p inc;",
|
"command": "true;rm -rf inc/* inc/.hist/;mkdir -p inc;",
|
||||||
"type": "shell"
|
"type": "shell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "no_dbg",
|
||||||
|
"command": "${config:python.pythonPath} -m copyparty -ed -emp -e2d -e2s -a ed:wark -v srv::r:aed:cnodupe ;exit 1",
|
||||||
|
"type": "shell"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
turn your phone or raspi into a portable file server with resumable uploads/downloads using IE6 or any other browser
|
turn your phone or raspi into a portable file server with resumable uploads/downloads using IE6 or any other browser
|
||||||
|
|
||||||
* server runs on anything with `py2.7` or `py3.2+`
|
* server runs on anything with `py2.7` or `py3.3+`
|
||||||
* *resumable* uploads need `firefox 12+` / `chrome 6+` / `safari 6+` / `IE 10+`
|
* *resumable* uploads need `firefox 12+` / `chrome 6+` / `safari 6+` / `IE 10+`
|
||||||
* code standard: `black`
|
* code standard: `black`
|
||||||
|
|
||||||
@@ -68,17 +68,16 @@ summary: it works! you can use it! (but technically not even close to beta)
|
|||||||
# dependencies
|
# dependencies
|
||||||
|
|
||||||
* `jinja2`
|
* `jinja2`
|
||||||
* pulls in `markupsafe` as of v2.7; use jinja 2.6 on py3.2
|
|
||||||
|
|
||||||
optional, enables thumbnails:
|
optional, will eventually enable thumbnails:
|
||||||
* `Pillow` (requires py2.7 or py3.5+)
|
* `Pillow` (requires py2.7 or py3.5+)
|
||||||
|
|
||||||
|
|
||||||
# sfx
|
# sfx
|
||||||
|
|
||||||
currently there are two self-contained binaries:
|
currently there are two self-contained binaries:
|
||||||
* `copyparty-sfx.sh` for unix (linux and osx) -- smaller, more robust
|
* [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) -- pure python, works everywhere
|
||||||
* `copyparty-sfx.py` for windows (unix too) -- crossplatform, beta
|
* [copyparty-sfx.sh](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.sh) -- smaller, but only for linux and macos
|
||||||
|
|
||||||
launch either of them (**use sfx.py on systemd**) and it'll unpack and run copyparty, assuming you have python installed of course
|
launch either of them (**use sfx.py on systemd**) and it'll unpack and run copyparty, assuming you have python installed of course
|
||||||
|
|
||||||
|
|||||||
@@ -1067,7 +1067,7 @@ def main():
|
|||||||
dbg = null_log
|
dbg = null_log
|
||||||
|
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
os.system("")
|
os.system("rem")
|
||||||
|
|
||||||
for ch in '<>:"\\|?*':
|
for ch in '<>:"\\|?*':
|
||||||
# microsoft maps illegal characters to f0xx
|
# microsoft maps illegal characters to f0xx
|
||||||
|
|||||||
@@ -980,7 +980,7 @@ def main():
|
|||||||
dbg = null_log
|
dbg = null_log
|
||||||
|
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
os.system("")
|
os.system("rem")
|
||||||
|
|
||||||
for ch in '<>:"\\|?*':
|
for ch in '<>:"\\|?*':
|
||||||
# microsoft maps illegal characters to f0xx
|
# microsoft maps illegal characters to f0xx
|
||||||
|
|||||||
@@ -10,7 +10,12 @@
|
|||||||
* modify `10.13.1.1` as necessary if you wish to support browsers without javascript
|
* modify `10.13.1.1` as necessary if you wish to support browsers without javascript
|
||||||
|
|
||||||
### [`explorer-nothumbs-nofoldertypes.reg`](explorer-nothumbs-nofoldertypes.reg)
|
### [`explorer-nothumbs-nofoldertypes.reg`](explorer-nothumbs-nofoldertypes.reg)
|
||||||
disables thumbnails and folder-type detection in windows explorer, makes it way faster (especially for slow/networked locations (such as copyparty-fuse))
|
* disables thumbnails and folder-type detection in windows explorer
|
||||||
|
* makes it way faster (especially for slow/networked locations (such as copyparty-fuse))
|
||||||
|
|
||||||
|
### [`cfssl.sh`](cfssl.sh)
|
||||||
|
* creates CA and server certificates using cfssl
|
||||||
|
* give a 3rd argument to install it to your copyparty config
|
||||||
|
|
||||||
# OS integration
|
# OS integration
|
||||||
init-scripts to start copyparty as a service
|
init-scripts to start copyparty as a service
|
||||||
|
|||||||
72
contrib/cfssl.sh
Executable file
72
contrib/cfssl.sh
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ca-name and server-name
|
||||||
|
ca_name="$1"
|
||||||
|
srv_name="$2"
|
||||||
|
|
||||||
|
[ -z "$srv_name" ] && {
|
||||||
|
echo "need arg 1: ca name"
|
||||||
|
echo "need arg 2: server name"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gen_ca() {
|
||||||
|
(tee /dev/stderr <<EOF
|
||||||
|
{"CN": "$ca_name ca",
|
||||||
|
"CA": {"expiry":"87600h", "pathlen":0},
|
||||||
|
"key": {"algo":"rsa", "size":4096},
|
||||||
|
"names": [{"O":"$ca_name ca"}]}
|
||||||
|
EOF
|
||||||
|
)|
|
||||||
|
cfssl gencert -initca - |
|
||||||
|
cfssljson -bare ca
|
||||||
|
|
||||||
|
mv ca-key.pem ca.key
|
||||||
|
rm ca.csr
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gen_srv() {
|
||||||
|
(tee /dev/stderr <<EOF
|
||||||
|
{"key": {"algo":"rsa", "size":4096},
|
||||||
|
"names": [{"O":"$ca_name - $srv_name"}]}
|
||||||
|
EOF
|
||||||
|
)|
|
||||||
|
cfssl gencert -ca ca.pem -ca-key ca.key \
|
||||||
|
-profile=www -hostname="$srv_name.$ca_name" - |
|
||||||
|
cfssljson -bare "$srv_name"
|
||||||
|
|
||||||
|
mv "$srv_name-key.pem" "$srv_name.key"
|
||||||
|
rm "$srv_name.csr"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# create ca if not exist
|
||||||
|
[ -e ca.key ] ||
|
||||||
|
gen_ca
|
||||||
|
|
||||||
|
# always create server cert
|
||||||
|
gen_srv
|
||||||
|
|
||||||
|
|
||||||
|
# dump cert info
|
||||||
|
show() {
|
||||||
|
openssl x509 -text -noout -in $1 |
|
||||||
|
awk '!o; {o=0} /[0-9a-f:]{16}/{o=1}'
|
||||||
|
}
|
||||||
|
show ca.pem
|
||||||
|
show "$srv_name.pem"
|
||||||
|
|
||||||
|
|
||||||
|
# write cert into copyparty config
|
||||||
|
[ -z "$3" ] || {
|
||||||
|
mkdir -p ~/.config/copyparty
|
||||||
|
cat "$srv_name".{key,pem} ca.pem >~/.config/copyparty/cert.pem
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# rm *.key *.pem
|
||||||
|
# cfssl print-defaults config
|
||||||
|
# cfssl print-defaults csr
|
||||||
@@ -8,7 +8,9 @@ __copyright__ = 2019
|
|||||||
__license__ = "MIT"
|
__license__ = "MIT"
|
||||||
__url__ = "https://github.com/9001/copyparty/"
|
__url__ = "https://github.com/9001/copyparty/"
|
||||||
|
|
||||||
|
import re
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import shutil
|
import shutil
|
||||||
import filecmp
|
import filecmp
|
||||||
@@ -19,7 +21,13 @@ from textwrap import dedent
|
|||||||
from .__init__ import E, WINDOWS, VT100
|
from .__init__ import E, WINDOWS, VT100
|
||||||
from .__version__ import S_VERSION, S_BUILD_DT, CODENAME
|
from .__version__ import S_VERSION, S_BUILD_DT, CODENAME
|
||||||
from .svchub import SvcHub
|
from .svchub import SvcHub
|
||||||
from .util import py_desc
|
from .util import py_desc, align_tab
|
||||||
|
|
||||||
|
HAVE_SSL = True
|
||||||
|
try:
|
||||||
|
import ssl
|
||||||
|
except:
|
||||||
|
HAVE_SSL = False
|
||||||
|
|
||||||
|
|
||||||
class RiceFormatter(argparse.HelpFormatter):
|
class RiceFormatter(argparse.HelpFormatter):
|
||||||
@@ -85,10 +93,77 @@ def ensure_cert():
|
|||||||
# printf 'NO\n.\n.\n.\n.\ncopyparty-insecure\n.\n' | faketime '2000-01-01 00:00:00' openssl req -x509 -sha256 -newkey rsa:2048 -keyout insecure.pem -out insecure.pem -days $((($(printf %d 0x7fffffff)-$(date +%s --date=2000-01-01T00:00:00Z))/(60*60*24))) -nodes && ls -al insecure.pem && openssl x509 -in insecure.pem -text -noout
|
# printf 'NO\n.\n.\n.\n.\ncopyparty-insecure\n.\n' | faketime '2000-01-01 00:00:00' openssl req -x509 -sha256 -newkey rsa:2048 -keyout insecure.pem -out insecure.pem -days $((($(printf %d 0x7fffffff)-$(date +%s --date=2000-01-01T00:00:00Z))/(60*60*24))) -nodes && ls -al insecure.pem && openssl x509 -in insecure.pem -text -noout
|
||||||
|
|
||||||
|
|
||||||
|
def configure_ssl_ver(al):
|
||||||
|
def terse_sslver(txt):
|
||||||
|
txt = txt.lower()
|
||||||
|
for c in ["_", "v", "."]:
|
||||||
|
txt = txt.replace(c, "")
|
||||||
|
|
||||||
|
return txt.replace("tls10", "tls1")
|
||||||
|
|
||||||
|
# oh man i love openssl
|
||||||
|
# check this out
|
||||||
|
# hold my beer
|
||||||
|
ptn = re.compile(r"^OP_NO_(TLS|SSL)v")
|
||||||
|
sslver = terse_sslver(al.ssl_ver).split(",")
|
||||||
|
flags = [k for k in ssl.__dict__ if ptn.match(k)]
|
||||||
|
# SSLv2 SSLv3 TLSv1 TLSv1_1 TLSv1_2 TLSv1_3
|
||||||
|
if "help" in sslver:
|
||||||
|
avail = [terse_sslver(x[6:]) for x in flags]
|
||||||
|
avail = " ".join(sorted(avail) + ["all"])
|
||||||
|
print("\navailable ssl/tls versions:\n " + avail)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
al.ssl_flags_en = 0
|
||||||
|
al.ssl_flags_de = 0
|
||||||
|
for flag in sorted(flags):
|
||||||
|
ver = terse_sslver(flag[6:])
|
||||||
|
num = getattr(ssl, flag)
|
||||||
|
if ver in sslver:
|
||||||
|
al.ssl_flags_en |= num
|
||||||
|
else:
|
||||||
|
al.ssl_flags_de |= num
|
||||||
|
|
||||||
|
if sslver == ["all"]:
|
||||||
|
x = al.ssl_flags_en
|
||||||
|
al.ssl_flags_en = al.ssl_flags_de
|
||||||
|
al.ssl_flags_de = x
|
||||||
|
|
||||||
|
for k in ["ssl_flags_en", "ssl_flags_de"]:
|
||||||
|
num = getattr(al, k)
|
||||||
|
print("{}: {:8x} ({})".format(k, num, num))
|
||||||
|
|
||||||
|
# think i need that beer now
|
||||||
|
|
||||||
|
|
||||||
|
def configure_ssl_ciphers(al):
|
||||||
|
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
|
if al.ssl_ver:
|
||||||
|
ctx.options &= ~al.ssl_flags_en
|
||||||
|
ctx.options |= al.ssl_flags_de
|
||||||
|
|
||||||
|
is_help = al.ciphers == "help"
|
||||||
|
|
||||||
|
if al.ciphers and not is_help:
|
||||||
|
try:
|
||||||
|
ctx.set_ciphers(al.ciphers)
|
||||||
|
except:
|
||||||
|
print("\n\033[1;31mfailed to set ciphers\033[0m\n")
|
||||||
|
|
||||||
|
if not hasattr(ctx, "get_ciphers"):
|
||||||
|
print("cannot read cipher list: openssl or python too old")
|
||||||
|
else:
|
||||||
|
ciphers = [x["description"] for x in ctx.get_ciphers()]
|
||||||
|
print("\n ".join(["\nenabled ciphers:"] + align_tab(ciphers) + [""]))
|
||||||
|
|
||||||
|
if is_help:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
time.strptime("19970815", "%Y%m%d") # python#7980
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
os.system("") # enables colors
|
os.system("rem") # enables colors
|
||||||
|
|
||||||
desc = py_desc().replace("[", "\033[1;30m[")
|
desc = py_desc().replace("[", "\033[1;30m[")
|
||||||
|
|
||||||
@@ -96,6 +171,7 @@ def main():
|
|||||||
print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc))
|
print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc))
|
||||||
|
|
||||||
ensure_locale()
|
ensure_locale()
|
||||||
|
if HAVE_SSL:
|
||||||
ensure_cert()
|
ensure_cert()
|
||||||
|
|
||||||
ap = argparse.ArgumentParser(
|
ap = argparse.ArgumentParser(
|
||||||
@@ -127,6 +203,16 @@ def main():
|
|||||||
|
|
||||||
consider the config file for more flexible account/volume management,
|
consider the config file for more flexible account/volume management,
|
||||||
including dynamic reload at runtime (and being more readable w)
|
including dynamic reload at runtime (and being more readable w)
|
||||||
|
|
||||||
|
values for --urlform:
|
||||||
|
"stash" dumps the data to file and returns length + checksum
|
||||||
|
"save,get" dumps to file and returns the page like a GET
|
||||||
|
"print,get" prints the data in the log and returns GET
|
||||||
|
(leave out the ",get" to return an error instead)
|
||||||
|
|
||||||
|
--ciphers help = available ssl/tls ciphers,
|
||||||
|
--ssl-ver help = available ssl/tls versions,
|
||||||
|
default is what python considers safe, usually >= TLS1
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -148,6 +234,15 @@ def main():
|
|||||||
ap.add_argument("-nih", action="store_true", help="no info hostname")
|
ap.add_argument("-nih", action="store_true", help="no info hostname")
|
||||||
ap.add_argument("-nid", action="store_true", help="no info disk-usage")
|
ap.add_argument("-nid", action="store_true", help="no info disk-usage")
|
||||||
ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile")
|
ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile")
|
||||||
|
ap.add_argument("--urlform", type=str, default="print,get", help="how to handle url-forms")
|
||||||
|
|
||||||
|
ap2 = ap.add_argument_group('SSL/TLS options')
|
||||||
|
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls")
|
||||||
|
ap2.add_argument("--https-only", action="store_true", help="disable plaintext")
|
||||||
|
ap2.add_argument("--ssl-ver", type=str, help="ssl/tls versions to allow")
|
||||||
|
ap2.add_argument("--ciphers", metavar="LIST", help="set allowed ciphers")
|
||||||
|
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
||||||
|
ap2.add_argument("--ssl-log", metavar="PATH", help="log master secrets")
|
||||||
al = ap.parse_args()
|
al = ap.parse_args()
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
@@ -161,6 +256,15 @@ def main():
|
|||||||
except:
|
except:
|
||||||
raise Exception("invalid value for -p")
|
raise Exception("invalid value for -p")
|
||||||
|
|
||||||
|
if HAVE_SSL:
|
||||||
|
if al.ssl_ver:
|
||||||
|
configure_ssl_ver(al)
|
||||||
|
|
||||||
|
if al.ciphers:
|
||||||
|
configure_ssl_ciphers(al)
|
||||||
|
else:
|
||||||
|
print("\033[33m ssl module does not exist; cannot enable https\033[0m\n")
|
||||||
|
|
||||||
SvcHub(al).run()
|
SvcHub(al).run()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 7, 3)
|
VERSION = (0, 7, 6)
|
||||||
CODENAME = "keeping track"
|
CODENAME = "keeping track"
|
||||||
BUILD_DT = (2021, 2, 3)
|
BUILD_DT = (2021, 2, 12)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -134,6 +134,16 @@ class HttpCli(object):
|
|||||||
uparam["raw"] = True
|
uparam["raw"] = True
|
||||||
uparam["dots"] = True
|
uparam["dots"] = True
|
||||||
|
|
||||||
|
if hasattr(self.s, "cipher"):
|
||||||
|
self.ssl_suf = "".join(
|
||||||
|
[
|
||||||
|
" \033[3{}m{}".format(c, s)
|
||||||
|
for c, s in zip([6, 3, 6], self.s.cipher())
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.ssl_suf = ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.mode in ["GET", "HEAD"]:
|
if self.mode in ["GET", "HEAD"]:
|
||||||
return self.handle_get() and self.keepalive
|
return self.handle_get() and self.keepalive
|
||||||
@@ -211,7 +221,7 @@ class HttpCli(object):
|
|||||||
|
|
||||||
logmsg += " [\033[36m" + rval + "\033[0m]"
|
logmsg += " [\033[36m" + rval + "\033[0m]"
|
||||||
|
|
||||||
self.log(logmsg)
|
self.log(logmsg + self.ssl_suf)
|
||||||
|
|
||||||
# "embedded" resources
|
# "embedded" resources
|
||||||
if self.vpath.startswith(".cpr"):
|
if self.vpath.startswith(".cpr"):
|
||||||
@@ -245,7 +255,7 @@ class HttpCli(object):
|
|||||||
return self.tx_browser()
|
return self.tx_browser()
|
||||||
|
|
||||||
def handle_options(self):
|
def handle_options(self):
|
||||||
self.log("OPTIONS " + self.req)
|
self.log("OPTIONS " + self.req + self.ssl_suf)
|
||||||
self.send_headers(
|
self.send_headers(
|
||||||
None,
|
None,
|
||||||
204,
|
204,
|
||||||
@@ -258,7 +268,7 @@ class HttpCli(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def handle_put(self):
|
def handle_put(self):
|
||||||
self.log("PUT " + self.req)
|
self.log("PUT " + self.req + self.ssl_suf)
|
||||||
|
|
||||||
if self.headers.get("expect", "").lower() == "100-continue":
|
if self.headers.get("expect", "").lower() == "100-continue":
|
||||||
try:
|
try:
|
||||||
@@ -269,7 +279,7 @@ class HttpCli(object):
|
|||||||
return self.handle_stash()
|
return self.handle_stash()
|
||||||
|
|
||||||
def handle_post(self):
|
def handle_post(self):
|
||||||
self.log("POST " + self.req)
|
self.log("POST " + self.req + self.ssl_suf)
|
||||||
|
|
||||||
if self.headers.get("expect", "").lower() == "100-continue":
|
if self.headers.get("expect", "").lower() == "100-continue":
|
||||||
try:
|
try:
|
||||||
@@ -294,16 +304,37 @@ class HttpCli(object):
|
|||||||
if "application/octet-stream" in ctype:
|
if "application/octet-stream" in ctype:
|
||||||
return self.handle_post_binary()
|
return self.handle_post_binary()
|
||||||
|
|
||||||
raise Pebkac(405, "don't know how to handle {} POST".format(ctype))
|
if "application/x-www-form-urlencoded" in ctype:
|
||||||
|
opt = self.args.urlform
|
||||||
|
if "stash" in opt:
|
||||||
|
return self.handle_stash()
|
||||||
|
|
||||||
def handle_stash(self):
|
if "save" in opt:
|
||||||
|
post_sz, _, _, path = self.dump_to_file()
|
||||||
|
self.log("urlform: {} bytes, {}".format(post_sz, path))
|
||||||
|
elif "print" in opt:
|
||||||
|
reader, _ = self.get_body_reader()
|
||||||
|
for buf in reader:
|
||||||
|
buf = buf.decode("utf-8", "replace")
|
||||||
|
self.log("urlform:\n {}\n".format(buf))
|
||||||
|
|
||||||
|
if "get" in opt:
|
||||||
|
return self.handle_get()
|
||||||
|
|
||||||
|
raise Pebkac(405, "POST({}) is disabled".format(ctype))
|
||||||
|
|
||||||
|
raise Pebkac(405, "don't know how to handle POST({})".format(ctype))
|
||||||
|
|
||||||
|
def get_body_reader(self):
|
||||||
remains = int(self.headers.get("content-length", None))
|
remains = int(self.headers.get("content-length", None))
|
||||||
if remains is None:
|
if remains is None:
|
||||||
reader = read_socket_unbounded(self.sr)
|
|
||||||
self.keepalive = False
|
self.keepalive = False
|
||||||
|
return read_socket_unbounded(self.sr), remains
|
||||||
else:
|
else:
|
||||||
reader = read_socket(self.sr, remains)
|
return read_socket(self.sr, remains), remains
|
||||||
|
|
||||||
|
def dump_to_file(self):
|
||||||
|
reader, remains = self.get_body_reader()
|
||||||
vfs, rem = self.conn.auth.vfs.get(self.vpath, self.uname, False, True)
|
vfs, rem = self.conn.auth.vfs.get(self.vpath, self.uname, False, True)
|
||||||
fdir = os.path.join(vfs.realpath, rem)
|
fdir = os.path.join(vfs.realpath, rem)
|
||||||
|
|
||||||
@@ -314,6 +345,10 @@ class HttpCli(object):
|
|||||||
with open(path, "wb", 512 * 1024) as f:
|
with open(path, "wb", 512 * 1024) as f:
|
||||||
post_sz, _, sha_b64 = hashcopy(self.conn, reader, f)
|
post_sz, _, sha_b64 = hashcopy(self.conn, reader, f)
|
||||||
|
|
||||||
|
return post_sz, sha_b64, remains, path
|
||||||
|
|
||||||
|
def handle_stash(self):
|
||||||
|
post_sz, sha_b64, remains, path = self.dump_to_file()
|
||||||
spd = self._spd(post_sz)
|
spd = self._spd(post_sz)
|
||||||
self.log("{} wrote {}/{} bytes to {}".format(spd, post_sz, remains, path))
|
self.log("{} wrote {}/{} bytes to {}".format(spd, post_sz, remains, path))
|
||||||
self.reply("{}\n{}\n".format(post_sz, sha_b64).encode("utf-8"))
|
self.reply("{}\n{}\n".format(post_sz, sha_b64).encode("utf-8"))
|
||||||
@@ -517,10 +552,9 @@ class HttpCli(object):
|
|||||||
raise Pebkac(500, "mkdir failed, check the logs")
|
raise Pebkac(500, "mkdir failed, check the logs")
|
||||||
|
|
||||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||||
|
esc_paths = [quotep(vpath), html_escape(vpath)]
|
||||||
html = self.conn.tpl_msg.render(
|
html = self.conn.tpl_msg.render(
|
||||||
h2='<a href="/{}">go to /{}</a>'.format(
|
h2='<a href="/{}">go to /{}</a>'.format(*esc_paths),
|
||||||
quotep(vpath), html_escape(vpath)
|
|
||||||
),
|
|
||||||
pre="aight",
|
pre="aight",
|
||||||
click=True,
|
click=True,
|
||||||
)
|
)
|
||||||
@@ -903,8 +937,11 @@ class HttpCli(object):
|
|||||||
open_func = open
|
open_func = open
|
||||||
# 512 kB is optimal for huge files, use 64k
|
# 512 kB is optimal for huge files, use 64k
|
||||||
open_args = [fsenc(fs_path), "rb", 64 * 1024]
|
open_args = [fsenc(fs_path), "rb", 64 * 1024]
|
||||||
if hasattr(os, "sendfile"):
|
use_sendfile = (
|
||||||
use_sendfile = not self.args.no_sendfile
|
not self.ssl_suf
|
||||||
|
and not self.args.no_sendfile
|
||||||
|
and hasattr(os, "sendfile")
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# send reply
|
# send reply
|
||||||
@@ -1058,6 +1095,10 @@ class HttpCli(object):
|
|||||||
if not self.args.ed or "dots" not in self.uparam:
|
if not self.args.ed or "dots" not in self.uparam:
|
||||||
vfs_ls = exclude_dotfiles(vfs_ls)
|
vfs_ls = exclude_dotfiles(vfs_ls)
|
||||||
|
|
||||||
|
hidden = []
|
||||||
|
if fsroot.endswith(str(os.sep) + ".hist"):
|
||||||
|
hidden = ["up2k.db", "up2k.snap"]
|
||||||
|
|
||||||
dirs = []
|
dirs = []
|
||||||
files = []
|
files = []
|
||||||
for fn in vfs_ls:
|
for fn in vfs_ls:
|
||||||
@@ -1069,6 +1110,8 @@ class HttpCli(object):
|
|||||||
|
|
||||||
if fn in vfs_virt:
|
if fn in vfs_virt:
|
||||||
fspath = vfs_virt[fn].realpath
|
fspath = vfs_virt[fn].realpath
|
||||||
|
elif fn in hidden:
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
fspath = fsroot + "/" + fn
|
fspath = fsroot + "/" + fn
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ from __future__ import print_function, unicode_literals
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import ssl
|
|
||||||
import time
|
import time
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
HAVE_SSL = True
|
||||||
|
try:
|
||||||
|
import ssl
|
||||||
|
except:
|
||||||
|
HAVE_SSL = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import jinja2
|
import jinja2
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -75,9 +80,8 @@ class HttpConn(object):
|
|||||||
def log(self, msg):
|
def log(self, msg):
|
||||||
self.log_func(self.log_src, msg)
|
self.log_func(self.log_src, msg)
|
||||||
|
|
||||||
def run(self):
|
def _detect_https(self):
|
||||||
method = None
|
method = None
|
||||||
self.sr = None
|
|
||||||
if self.cert_path:
|
if self.cert_path:
|
||||||
try:
|
try:
|
||||||
method = self.s.recv(4, socket.MSG_PEEK)
|
method = self.s.recv(4, socket.MSG_PEEK)
|
||||||
@@ -102,16 +106,52 @@ class HttpConn(object):
|
|||||||
self.s.send(b"HTTP/1.1 400 Bad Request\r\n\r\n" + err.encode("utf-8"))
|
self.s.send(b"HTTP/1.1 400 Bad Request\r\n\r\n" + err.encode("utf-8"))
|
||||||
return
|
return
|
||||||
|
|
||||||
if method not in [None, b"GET ", b"HEAD", b"POST", b"PUT ", b"OPTI"]:
|
return method not in [None, b"GET ", b"HEAD", b"POST", b"PUT ", b"OPTI"]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.sr = None
|
||||||
|
if self.args.https_only:
|
||||||
|
is_https = True
|
||||||
|
elif self.args.http_only or not HAVE_SSL:
|
||||||
|
is_https = False
|
||||||
|
else:
|
||||||
|
is_https = self._detect_https()
|
||||||
|
|
||||||
|
if is_https:
|
||||||
if self.sr:
|
if self.sr:
|
||||||
self.log("\033[1;31mTODO: cannot do https in jython\033[0m")
|
self.log("\033[1;31mTODO: cannot do https in jython\033[0m")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log_src = self.log_src.replace("[36m", "[35m")
|
self.log_src = self.log_src.replace("[36m", "[35m")
|
||||||
try:
|
try:
|
||||||
self.s = ssl.wrap_socket(
|
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
self.s, server_side=True, certfile=self.cert_path
|
ctx.load_cert_chain(self.cert_path)
|
||||||
)
|
if self.args.ssl_ver:
|
||||||
|
ctx.options &= ~self.args.ssl_flags_en
|
||||||
|
ctx.options |= self.args.ssl_flags_de
|
||||||
|
# print(repr(ctx.options))
|
||||||
|
|
||||||
|
if self.args.ssl_log:
|
||||||
|
try:
|
||||||
|
ctx.keylog_filename = self.args.ssl_log
|
||||||
|
except:
|
||||||
|
self.log("keylog failed; openssl or python too old")
|
||||||
|
|
||||||
|
if self.args.ciphers:
|
||||||
|
ctx.set_ciphers(self.args.ciphers)
|
||||||
|
|
||||||
|
self.s = ctx.wrap_socket(self.s, server_side=True)
|
||||||
|
if self.args.ssl_dbg and hasattr(self.s, "shared_ciphers"):
|
||||||
|
overlap = [y[::-1] for y in self.s.shared_ciphers()]
|
||||||
|
lines = [str(x) for x in (["TLS cipher overlap:"] + overlap)]
|
||||||
|
self.log("\n".join(lines))
|
||||||
|
for k, v in [
|
||||||
|
["compression", self.s.compression()],
|
||||||
|
["ALPN proto", self.s.selected_alpn_protocol()],
|
||||||
|
["NPN proto", self.s.selected_npn_protocol()],
|
||||||
|
]:
|
||||||
|
self.log("TLS {}: {}".format(k, v or "nah"))
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
em = str(ex)
|
em = str(ex)
|
||||||
|
|
||||||
|
|||||||
@@ -53,15 +53,13 @@ class TcpSrv(object):
|
|||||||
srv.bind((ip, port))
|
srv.bind((ip, port))
|
||||||
return srv
|
return srv
|
||||||
except (OSError, socket.error) as ex:
|
except (OSError, socket.error) as ex:
|
||||||
if ex.errno == 98:
|
if ex.errno in [98, 48]:
|
||||||
raise Exception(
|
e = "\033[1;31mport {} is busy on interface {}\033[0m".format(port, ip)
|
||||||
"\033[1;31mport {} is busy on interface {}\033[0m".format(port, ip)
|
elif ex.errno in [99, 49]:
|
||||||
)
|
e = "\033[1;31minterface {} does not exist\033[0m".format(ip)
|
||||||
|
else:
|
||||||
if ex.errno == 99:
|
raise
|
||||||
raise Exception(
|
raise Exception(e)
|
||||||
"\033[1;31minterface {} does not exist\033[0m".format(ip)
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for srv in self.srv:
|
for srv in self.srv:
|
||||||
|
|||||||
@@ -130,15 +130,19 @@ class Up2k(object):
|
|||||||
if db:
|
if db:
|
||||||
# can be symlink so don't `and d.startswith(top)``
|
# can be symlink so don't `and d.startswith(top)``
|
||||||
excl = set([d for d in tops if d != top])
|
excl = set([d for d in tops if d != top])
|
||||||
self._build_dir([db, 0], top, excl, top)
|
dbw = [db, 0, time.time()]
|
||||||
|
self._build_dir(dbw, top, excl, top)
|
||||||
self._drop_lost(db, top)
|
self._drop_lost(db, top)
|
||||||
|
if dbw[1]:
|
||||||
|
self.log("up2k", "commit {} new files".format(dbw[1]))
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
def _build_dir(self, dbw, top, excl, cdir):
|
def _build_dir(self, dbw, top, excl, cdir):
|
||||||
try:
|
try:
|
||||||
inodes = [fsdec(x) for x in os.listdir(fsenc(cdir))]
|
inodes = [fsdec(x) for x in os.listdir(fsenc(cdir))]
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log("up2k", "listdir: " + repr(ex))
|
self.log("up2k", "listdir: {} @ [{}]".format(repr(ex), cdir))
|
||||||
return
|
return
|
||||||
|
|
||||||
histdir = os.path.join(top, ".hist")
|
histdir = os.path.join(top, ".hist")
|
||||||
@@ -147,7 +151,7 @@ class Up2k(object):
|
|||||||
try:
|
try:
|
||||||
inf = os.stat(fsenc(abspath))
|
inf = os.stat(fsenc(abspath))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log("up2k", "stat: " + repr(ex))
|
self.log("up2k", "stat: {} @ [{}]".format(repr(ex), abspath))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if stat.S_ISDIR(inf.st_mode):
|
if stat.S_ISDIR(inf.st_mode):
|
||||||
@@ -182,15 +186,18 @@ class Up2k(object):
|
|||||||
try:
|
try:
|
||||||
hashes = self._hashlist_from_file(abspath)
|
hashes = self._hashlist_from_file(abspath)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log("up2k", "hash: " + repr(ex))
|
self.log("up2k", "hash: {} @ [{}]".format(repr(ex), abspath))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
wark = self._wark_from_hashlist(inf.st_size, hashes)
|
wark = self._wark_from_hashlist(inf.st_size, hashes)
|
||||||
self.db_add(dbw[0], wark, rp, inf.st_mtime, inf.st_size)
|
self.db_add(dbw[0], wark, rp, inf.st_mtime, inf.st_size)
|
||||||
dbw[1] += 1
|
dbw[1] += 1
|
||||||
if dbw[1] > 1024:
|
td = time.time() - dbw[2]
|
||||||
|
if dbw[1] > 1024 or td > 60:
|
||||||
|
self.log("up2k", "commit {} new files".format(dbw[1]))
|
||||||
dbw[0].commit()
|
dbw[0].commit()
|
||||||
dbw[1] = 0
|
dbw[1] = 0
|
||||||
|
dbw[2] = time.time()
|
||||||
|
|
||||||
def _drop_lost(self, db, top):
|
def _drop_lost(self, db, top):
|
||||||
rm = []
|
rm = []
|
||||||
@@ -201,7 +208,7 @@ class Up2k(object):
|
|||||||
if not os.path.exists(fsenc(abspath)):
|
if not os.path.exists(fsenc(abspath)):
|
||||||
rm.append(drp)
|
rm.append(drp)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log("up2k", "stat-rm: " + repr(ex))
|
self.log("up2k", "stat-rm: {} @ [{}]".format(repr(ex), abspath))
|
||||||
|
|
||||||
if not rm:
|
if not rm:
|
||||||
return
|
return
|
||||||
@@ -512,8 +519,15 @@ class Up2k(object):
|
|||||||
fsz = os.path.getsize(path)
|
fsz = os.path.getsize(path)
|
||||||
csz = self._get_chunksize(fsz)
|
csz = self._get_chunksize(fsz)
|
||||||
ret = []
|
ret = []
|
||||||
|
last_print = time.time()
|
||||||
with open(path, "rb", 512 * 1024) as f:
|
with open(path, "rb", 512 * 1024) as f:
|
||||||
while fsz > 0:
|
while fsz > 0:
|
||||||
|
now = time.time()
|
||||||
|
td = now - last_print
|
||||||
|
if td >= 0.3:
|
||||||
|
last_print = now
|
||||||
|
print(" {} \n\033[A".format(fsz), end="")
|
||||||
|
|
||||||
hashobj = hashlib.sha512()
|
hashobj = hashlib.sha512()
|
||||||
rem = min(csz, fsz)
|
rem = min(csz, fsz)
|
||||||
fsz -= rem
|
fsz -= rem
|
||||||
|
|||||||
@@ -718,6 +718,22 @@ def py_desc():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def align_tab(lines):
|
||||||
|
rows = []
|
||||||
|
ncols = 0
|
||||||
|
for ln in lines:
|
||||||
|
row = [x for x in ln.split(" ") if x]
|
||||||
|
ncols = max(ncols, len(row))
|
||||||
|
rows.append(row)
|
||||||
|
|
||||||
|
lens = [0] * ncols
|
||||||
|
for row in rows:
|
||||||
|
for n, col in enumerate(row):
|
||||||
|
lens[n] = max(lens[n], len(col))
|
||||||
|
|
||||||
|
return ["".join(x.ljust(y + 2) for x, y in zip(row, lens)) for row in rows]
|
||||||
|
|
||||||
|
|
||||||
class Pebkac(Exception):
|
class Pebkac(Exception):
|
||||||
def __init__(self, code, msg=None):
|
def __init__(self, code, msg=None):
|
||||||
super(Pebkac, self).__init__(msg or HTTPCODE[code])
|
super(Pebkac, self).__init__(msg or HTTPCODE[code])
|
||||||
|
|||||||
@@ -219,6 +219,10 @@ function up2k_init(have_crypto) {
|
|||||||
"hash": [],
|
"hash": [],
|
||||||
"handshake": [],
|
"handshake": [],
|
||||||
"upload": []
|
"upload": []
|
||||||
|
},
|
||||||
|
"bytes": {
|
||||||
|
"hashed": 0,
|
||||||
|
"uploaded": 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -357,8 +361,11 @@ function up2k_init(have_crypto) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hashing_permitted() {
|
function hashing_permitted() {
|
||||||
var lim = multitask ? 1 : 0;
|
if (multitask) {
|
||||||
return handshakes_permitted() && lim >=
|
var ahead = st.bytes.hashed - st.bytes.uploaded;
|
||||||
|
return ahead < 1024 * 1024 * 128;
|
||||||
|
}
|
||||||
|
return handshakes_permitted() && 0 ==
|
||||||
st.todo.handshake.length +
|
st.todo.handshake.length +
|
||||||
st.busy.handshake.length;
|
st.busy.handshake.length;
|
||||||
}
|
}
|
||||||
@@ -512,6 +519,7 @@ function up2k_init(have_crypto) {
|
|||||||
|
|
||||||
var t = st.todo.hash.shift();
|
var t = st.todo.hash.shift();
|
||||||
st.busy.hash.push(t);
|
st.busy.hash.push(t);
|
||||||
|
st.bytes.hashed += t.size;
|
||||||
t.t1 = new Date().getTime();
|
t.t1 = new Date().getTime();
|
||||||
|
|
||||||
var nchunk = 0;
|
var nchunk = 0;
|
||||||
@@ -752,6 +760,7 @@ function up2k_init(have_crypto) {
|
|||||||
xhr.onload = function (xev) {
|
xhr.onload = function (xev) {
|
||||||
if (xhr.status == 200) {
|
if (xhr.status == 200) {
|
||||||
prog(t.n, npart, col_uploaded);
|
prog(t.n, npart, col_uploaded);
|
||||||
|
st.bytes.uploaded += cdr - car;
|
||||||
st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
|
st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
|
||||||
t.postlist.splice(t.postlist.indexOf(npart), 1);
|
t.postlist.splice(t.postlist.indexOf(npart), 1);
|
||||||
if (t.postlist.length == 0) {
|
if (t.postlist.length == 0) {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
href="#" data-dest="up2k">up2k</a><i></i><a
|
href="#" data-dest="up2k">up2k</a><i></i><a
|
||||||
href="#" data-dest="bup">bup</a><i></i><a
|
href="#" data-dest="bup">bup</a><i></i><a
|
||||||
href="#" data-dest="mkdir">mkdir</a><i></i><a
|
href="#" data-dest="mkdir">mkdir</a><i></i><a
|
||||||
href="#" data-dest="new_md">new.md</a></div>
|
href="#" data-dest="new_md">new.md</a><i></i><a
|
||||||
|
href="#" data-dest="msg">msg</a></div>
|
||||||
|
|
||||||
<div id="op_bup" class="opview opbox act">
|
<div id="op_bup" class="opview opbox act">
|
||||||
<div id="u2err"></div>
|
<div id="u2err"></div>
|
||||||
@@ -30,6 +31,13 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="op_msg" class="opview opbox">
|
||||||
|
<form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="/{{ vdir }}">
|
||||||
|
<input type="text" name="msg" size="30">
|
||||||
|
<input type="submit" value="send">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="op_up2k" class="opview">
|
<div id="op_up2k" class="opview">
|
||||||
<form id="u2form" method="post" enctype="multipart/form-data" onsubmit="return false;"></form>
|
<form id="u2form" method="post" enctype="multipart/form-data" onsubmit="return false;"></form>
|
||||||
|
|
||||||
|
|||||||
@@ -62,28 +62,32 @@ cd sfx
|
|||||||
)/pe-copyparty"
|
)/pe-copyparty"
|
||||||
|
|
||||||
echo "repack of files in $old"
|
echo "repack of files in $old"
|
||||||
cp -pR "$old/"*{jinja2,copyparty} .
|
cp -pR "$old/"*{dep-j2,copyparty} .
|
||||||
mv {x.,}jinja2 2>/dev/null || true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ $repack ] || {
|
[ $repack ] || {
|
||||||
echo collecting jinja2
|
echo collecting jinja2
|
||||||
f="../build/Jinja2-2.6.tar.gz"
|
f="../build/Jinja2-2.11.3.tar.gz"
|
||||||
[ -e "$f" ] ||
|
[ -e "$f" ] ||
|
||||||
(url=https://files.pythonhosted.org/packages/25/c8/212b1c2fd6df9eaf536384b6c6619c4e70a3afd2dffdd00e5296ffbae940/Jinja2-2.6.tar.gz;
|
(url=https://files.pythonhosted.org/packages/4f/e7/65300e6b32e69768ded990494809106f87da1d436418d5f1367ed3966fd7/Jinja2-2.11.3.tar.gz;
|
||||||
wget -O$f "$url" || curl -L "$url" >$f)
|
wget -O$f "$url" || curl -L "$url" >$f)
|
||||||
|
|
||||||
tar -zxf $f
|
tar -zxf $f
|
||||||
mv Jinja2-*/jinja2 .
|
mv Jinja2-*/src/jinja2 .
|
||||||
rm -rf Jinja2-* jinja2/testsuite jinja2/_markupsafe/tests.py jinja2/_stringdefs.py
|
rm -rf Jinja2-*
|
||||||
|
|
||||||
f=jinja2/lexer.py
|
echo collecting markupsafe
|
||||||
sed -r '/.*föö.*/ raise SyntaxError/' <$f >t
|
f="../build/MarkupSafe-1.1.1.tar.gz"
|
||||||
tmv $f
|
[ -e "$f" ] ||
|
||||||
|
(url=https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz;
|
||||||
|
wget -O$f "$url" || curl -L "$url" >$f)
|
||||||
|
|
||||||
f=jinja2/_markupsafe/_constants.py
|
tar -zxf $f
|
||||||
awk '!/: [0-9]+,?$/ || /(amp|gt|lt|quot|apos|nbsp).:/' <$f >t
|
mv MarkupSafe-*/src/markupsafe .
|
||||||
tmv $f
|
rm -rf MarkupSafe-* markupsafe/_speedups.c
|
||||||
|
|
||||||
|
mkdir dep-j2/
|
||||||
|
mv {markupsafe,jinja2} dep-j2/
|
||||||
|
|
||||||
# msys2 tar is bad, make the best of it
|
# msys2 tar is bad, make the best of it
|
||||||
echo collecting source
|
echo collecting source
|
||||||
@@ -165,6 +169,15 @@ done
|
|||||||
sed -r '/edit2">edit \(fancy/d' <$f >t && tmv "$f"
|
sed -r '/edit2">edit \(fancy/d' <$f >t && tmv "$f"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find | grep -E '\.py$' |
|
||||||
|
grep -vE '__version__' |
|
||||||
|
tr '\n' '\0' |
|
||||||
|
xargs -0 python ../scripts/uncomment.py
|
||||||
|
|
||||||
|
f=dep-j2/jinja2/constants.py
|
||||||
|
awk '/^LOREM_IPSUM_WORDS/{o=1;print "LOREM_IPSUM_WORDS = u\"a\"";next} !o; /"""/{o=0}' <$f >t
|
||||||
|
tmv "$f"
|
||||||
|
|
||||||
# up2k goes from 28k to 22k laff
|
# up2k goes from 28k to 22k laff
|
||||||
echo entabbening
|
echo entabbening
|
||||||
find | grep -E '\.(js|css|html|py)$' | while IFS= read -r f; do
|
find | grep -E '\.(js|css|html|py)$' | while IFS= read -r f; do
|
||||||
@@ -177,7 +190,7 @@ args=(--owner=1000 --group=1000)
|
|||||||
[ "$OSTYPE" = msys ] &&
|
[ "$OSTYPE" = msys ] &&
|
||||||
args=()
|
args=()
|
||||||
|
|
||||||
tar -cf tar "${args[@]}" --numeric-owner copyparty jinja2
|
tar -cf tar "${args[@]}" --numeric-owner copyparty dep-j2
|
||||||
|
|
||||||
echo compressing tar
|
echo compressing tar
|
||||||
# detect best level; bzip2 -7 is usually better than -9
|
# detect best level; bzip2 -7 is usually better than -9
|
||||||
|
|||||||
149
scripts/sfx.py
149
scripts/sfx.py
@@ -2,7 +2,7 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
import re, os, sys, time, shutil, signal, tarfile, hashlib, platform, tempfile
|
import os, sys, time, shutil, signal, tarfile, hashlib, platform, tempfile
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -202,93 +202,6 @@ def u8(gen):
|
|||||||
yield s
|
yield s
|
||||||
|
|
||||||
|
|
||||||
def get_py_win(ret):
|
|
||||||
tops = []
|
|
||||||
p = str(os.getenv("LocalAppdata"))
|
|
||||||
if p:
|
|
||||||
tops.append(os.path.join(p, "Programs", "Python"))
|
|
||||||
|
|
||||||
progfiles = {}
|
|
||||||
for p in ["ProgramFiles", "ProgramFiles(x86)"]:
|
|
||||||
p = str(os.getenv(p))
|
|
||||||
if p:
|
|
||||||
progfiles[p] = 1
|
|
||||||
# 32bit apps get x86 for both
|
|
||||||
if p.endswith(" (x86)"):
|
|
||||||
progfiles[p[:-6]] = 1
|
|
||||||
|
|
||||||
tops += list(progfiles.keys())
|
|
||||||
|
|
||||||
for sysroot in [me, sys.executable]:
|
|
||||||
sysroot = sysroot[:3].upper()
|
|
||||||
if sysroot[1] == ":" and sysroot not in tops:
|
|
||||||
tops.append(sysroot)
|
|
||||||
|
|
||||||
# $WIRESHARK_SLOGAN
|
|
||||||
for top in tops:
|
|
||||||
try:
|
|
||||||
for name1 in u8(sorted(os.listdir(top), reverse=True)):
|
|
||||||
if name1.lower().startswith("python"):
|
|
||||||
path1 = os.path.join(top, name1)
|
|
||||||
try:
|
|
||||||
for name2 in u8(os.listdir(path1)):
|
|
||||||
if name2.lower() == "python.exe":
|
|
||||||
path2 = os.path.join(path1, name2)
|
|
||||||
ret[path2.lower()] = path2
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_py_nix(ret):
|
|
||||||
ptn = re.compile(r"^(python|pypy)[0-9\.-]*$")
|
|
||||||
for bindir in os.getenv("PATH").split(":"):
|
|
||||||
if not bindir:
|
|
||||||
next
|
|
||||||
|
|
||||||
try:
|
|
||||||
for fn in u8(os.listdir(bindir)):
|
|
||||||
if ptn.match(fn):
|
|
||||||
fn = os.path.join(bindir, fn)
|
|
||||||
ret[fn.lower()] = fn
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def read_py(binp):
|
|
||||||
cmd = [
|
|
||||||
binp,
|
|
||||||
"-c",
|
|
||||||
"import sys; sys.stdout.write(' '.join(str(x) for x in sys.version_info)); import jinja2",
|
|
||||||
]
|
|
||||||
p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
|
|
||||||
ver, _ = p.communicate()
|
|
||||||
ver = ver.decode("utf-8").split(" ")[:3]
|
|
||||||
ver = [int(x) if x.isdigit() else 0 for x in ver]
|
|
||||||
return ver, p.returncode == 0
|
|
||||||
|
|
||||||
|
|
||||||
def get_pys():
|
|
||||||
ver, chk = read_py(sys.executable)
|
|
||||||
if chk or PY2:
|
|
||||||
return [[chk, ver, sys.executable]]
|
|
||||||
|
|
||||||
hits = {sys.executable.lower(): sys.executable}
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
get_py_win(hits)
|
|
||||||
else:
|
|
||||||
get_py_nix(hits)
|
|
||||||
|
|
||||||
ret = []
|
|
||||||
for binp in hits.values():
|
|
||||||
ver, chk = read_py(binp)
|
|
||||||
ret.append([chk, ver, binp])
|
|
||||||
msg("\t".join(str(x) for x in ret[-1]))
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def yieldfile(fn):
|
def yieldfile(fn):
|
||||||
with open(fn, "rb") as f:
|
with open(fn, "rb") as f:
|
||||||
for block in iter(lambda: f.read(64 * 1024), b""):
|
for block in iter(lambda: f.read(64 * 1024), b""):
|
||||||
@@ -440,12 +353,11 @@ def confirm():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def run(tmp, py):
|
def run(tmp, j2ver):
|
||||||
global cpp
|
global cpp
|
||||||
|
|
||||||
msg("OK")
|
msg("jinja2:", j2ver or "bundled")
|
||||||
msg("will use:", py)
|
msg("sfxdir:", tmp)
|
||||||
msg("bound to:", tmp)
|
|
||||||
|
|
||||||
# "systemd-tmpfiles-clean.timer"?? HOW do you even come up with this shit
|
# "systemd-tmpfiles-clean.timer"?? HOW do you even come up with this shit
|
||||||
try:
|
try:
|
||||||
@@ -457,24 +369,20 @@ def run(tmp, py):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
fp_py = os.path.join(tmp, "py")
|
ld = [tmp, os.path.join(tmp, "dep-j2")]
|
||||||
try:
|
if j2ver:
|
||||||
with open(fp_py, "wb") as f:
|
del ld[-1]
|
||||||
f.write(py.encode("utf-8") + b"\n")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# avoid loading ./copyparty.py
|
cmd = (
|
||||||
cmd = [
|
"import sys, runpy; "
|
||||||
py,
|
+ "".join(['sys.path.insert(0, r"' + x + '"); ' for x in ld])
|
||||||
"-c",
|
+ 'runpy.run_module("copyparty", run_name="__main__")'
|
||||||
'import sys, runpy; sys.path.insert(0, r"'
|
)
|
||||||
+ tmp
|
cmd = [sys.executable, "-c", cmd] + list(sys.argv[1:])
|
||||||
+ '"); runpy.run_module("copyparty", run_name="__main__")',
|
|
||||||
] + list(sys.argv[1:])
|
|
||||||
|
|
||||||
|
cmd = [str(x) for x in cmd]
|
||||||
msg("\n", cmd, "\n")
|
msg("\n", cmd, "\n")
|
||||||
cpp = sp.Popen(str(x) for x in cmd)
|
cpp = sp.Popen(cmd)
|
||||||
try:
|
try:
|
||||||
cpp.wait()
|
cpp.wait()
|
||||||
except:
|
except:
|
||||||
@@ -494,7 +402,6 @@ def bye(sig, frame):
|
|||||||
def main():
|
def main():
|
||||||
sysver = str(sys.version).replace("\n", "\n" + " " * 18)
|
sysver = str(sys.version).replace("\n", "\n" + " " * 18)
|
||||||
pktime = time.strftime("%Y-%m-%d, %H:%M:%S", time.gmtime(STAMP))
|
pktime = time.strftime("%Y-%m-%d, %H:%M:%S", time.gmtime(STAMP))
|
||||||
os.system("")
|
|
||||||
msg()
|
msg()
|
||||||
msg(" this is: copyparty", VER)
|
msg(" this is: copyparty", VER)
|
||||||
msg(" packed at:", pktime, "UTC,", STAMP)
|
msg(" packed at:", pktime, "UTC,", STAMP)
|
||||||
@@ -526,33 +433,13 @@ def main():
|
|||||||
signal.signal(signal.SIGTERM, bye)
|
signal.signal(signal.SIGTERM, bye)
|
||||||
|
|
||||||
tmp = unpack()
|
tmp = unpack()
|
||||||
fp_py = os.path.join(tmp, "py")
|
|
||||||
if os.path.exists(fp_py):
|
|
||||||
with open(fp_py, "rb") as f:
|
|
||||||
py = f.read().decode("utf-8").rstrip()
|
|
||||||
|
|
||||||
return run(tmp, py)
|
|
||||||
|
|
||||||
pys = get_pys()
|
|
||||||
pys.sort(reverse=True)
|
|
||||||
j2, ver, py = pys[0]
|
|
||||||
if j2:
|
|
||||||
try:
|
try:
|
||||||
os.rename(os.path.join(tmp, "jinja2"), os.path.join(tmp, "x.jinja2"))
|
from jinja2 import __version__ as j2ver
|
||||||
except:
|
except:
|
||||||
pass
|
j2ver = None
|
||||||
|
|
||||||
return run(tmp, py)
|
return run(tmp, j2ver)
|
||||||
|
|
||||||
msg("\n could not find jinja2; will use py2 + the bundled version\n")
|
|
||||||
for _, ver, py in pys:
|
|
||||||
if ver > [2, 7] and ver < [3, 0]:
|
|
||||||
return run(tmp, py)
|
|
||||||
|
|
||||||
m = "\033[1;31m\n\n\ncould not find a python with jinja2 installed; please do one of these:\n\n pip install --user jinja2\n\n install python2\n\n\033[0m"
|
|
||||||
msg(m)
|
|
||||||
confirm()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
77
scripts/uncomment.py
Normal file
77
scripts/uncomment.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding: utf-8
|
||||||
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
|
import io
|
||||||
|
import sys
|
||||||
|
import tokenize
|
||||||
|
|
||||||
|
|
||||||
|
def uncomment(fpath):
|
||||||
|
""" modified https://stackoverflow.com/a/62074206 """
|
||||||
|
|
||||||
|
with open(fpath, "rb") as f:
|
||||||
|
orig = f.read().decode("utf-8")
|
||||||
|
|
||||||
|
out = ""
|
||||||
|
for ln in orig.split("\n"):
|
||||||
|
if not ln.startswith("#"):
|
||||||
|
break
|
||||||
|
|
||||||
|
out += ln + "\n"
|
||||||
|
|
||||||
|
io_obj = io.StringIO(orig)
|
||||||
|
prev_toktype = tokenize.INDENT
|
||||||
|
last_lineno = -1
|
||||||
|
last_col = 0
|
||||||
|
for tok in tokenize.generate_tokens(io_obj.readline):
|
||||||
|
# print(repr(tok))
|
||||||
|
token_type = tok[0]
|
||||||
|
token_string = tok[1]
|
||||||
|
start_line, start_col = tok[2]
|
||||||
|
end_line, end_col = tok[3]
|
||||||
|
|
||||||
|
if start_line > last_lineno:
|
||||||
|
last_col = 0
|
||||||
|
|
||||||
|
if start_col > last_col:
|
||||||
|
out += " " * (start_col - last_col)
|
||||||
|
|
||||||
|
is_legalese = (
|
||||||
|
"copyright" in token_string.lower() or "license" in token_string.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
if token_type == tokenize.STRING:
|
||||||
|
if (
|
||||||
|
prev_toktype != tokenize.INDENT
|
||||||
|
and prev_toktype != tokenize.NEWLINE
|
||||||
|
and start_col > 0
|
||||||
|
or is_legalese
|
||||||
|
):
|
||||||
|
out += token_string
|
||||||
|
else:
|
||||||
|
out += '"a"'
|
||||||
|
elif token_type != tokenize.COMMENT or is_legalese:
|
||||||
|
out += token_string
|
||||||
|
|
||||||
|
prev_toktype = token_type
|
||||||
|
last_lineno = end_line
|
||||||
|
last_col = end_col
|
||||||
|
|
||||||
|
# out = "\n".join(x for x in out.splitlines() if x.strip())
|
||||||
|
|
||||||
|
with open(fpath, "wb") as f:
|
||||||
|
f.write(out.encode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("uncommenting", end="")
|
||||||
|
for f in sys.argv[1:]:
|
||||||
|
print(".", end="")
|
||||||
|
uncomment(f)
|
||||||
|
|
||||||
|
print("k")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
4
setup.py
4
setup.py
@@ -2,10 +2,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from glob import glob
|
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
|
||||||
setuptools_available = True
|
setuptools_available = True
|
||||||
@@ -110,13 +108,13 @@ args = {
|
|||||||
"Programming Language :: Python :: 2",
|
"Programming Language :: Python :: 2",
|
||||||
"Programming Language :: Python :: 2.7",
|
"Programming Language :: Python :: 2.7",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.2",
|
|
||||||
"Programming Language :: Python :: 3.3",
|
"Programming Language :: Python :: 3.3",
|
||||||
"Programming Language :: Python :: 3.4",
|
"Programming Language :: Python :: 3.4",
|
||||||
"Programming Language :: Python :: 3.5",
|
"Programming Language :: Python :: 3.5",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
"Environment :: Console",
|
"Environment :: Console",
|
||||||
|
|||||||
Reference in New Issue
Block a user