Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22cc22225a | ||
|
|
22dff4b0e5 | ||
|
|
a00ff2b086 | ||
|
|
e4acddc23b | ||
|
|
2b2d8e4e02 | ||
|
|
5501d49032 | ||
|
|
fa54b2eec4 | ||
|
|
cb0160021f | ||
|
|
93a723d588 | ||
|
|
8ebe1fb5e8 | ||
|
|
2acdf685b1 | ||
|
|
9f122ccd16 | ||
|
|
03be26fafc | ||
|
|
df5d309d6e | ||
|
|
c355f9bd91 | ||
|
|
9c28ba417e | ||
|
|
705b58c741 | ||
|
|
510302d667 | ||
|
|
025a537413 | ||
|
|
60a1ff0fc0 | ||
|
|
f94a0b1bff | ||
|
|
4ccfeeb2cd | ||
|
|
2646f6a4f2 | ||
|
|
b286ab539e | ||
|
|
2cca6e0922 | ||
|
|
db51f1b063 | ||
|
|
d979c47f50 | ||
|
|
e64b87b99b | ||
|
|
b985011a00 | ||
|
|
c2ed2314c8 | ||
|
|
cd496658c3 |
40
README.md
40
README.md
@@ -126,7 +126,7 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
|
|||||||
|
|
||||||
running copyparty without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; you may want [accounts and volumes](#accounts-and-volumes)
|
running copyparty without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; you may want [accounts and volumes](#accounts-and-volumes)
|
||||||
|
|
||||||
or see [complete windows example](./docs/examples/windows.md)
|
or see [some usage examples](#complete-examples) for inspiration, or the [complete windows example](./docs/examples/windows.md)
|
||||||
|
|
||||||
some recommended options:
|
some recommended options:
|
||||||
* `-e2dsa` enables general [file indexing](#file-indexing)
|
* `-e2dsa` enables general [file indexing](#file-indexing)
|
||||||
@@ -471,6 +471,7 @@ click the `🌲` or pressing the `B` hotkey to toggle between breadcrumbs path (
|
|||||||
## thumbnails
|
## thumbnails
|
||||||
|
|
||||||
press `g` or `田` to toggle grid-view instead of the file listing and `t` toggles icons / thumbnails
|
press `g` or `田` to toggle grid-view instead of the file listing and `t` toggles icons / thumbnails
|
||||||
|
* can be made default globally with `--grid` or per-volume with volflag `grid`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -481,6 +482,7 @@ it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video f
|
|||||||
audio files are covnerted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
|
audio files are covnerted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
|
||||||
|
|
||||||
images with the following names (see `--th-covers`) become the thumbnail of the folder they're in: `folder.png`, `folder.jpg`, `cover.png`, `cover.jpg`
|
images with the following names (see `--th-covers`) become the thumbnail of the folder they're in: `folder.png`, `folder.jpg`, `cover.png`, `cover.jpg`
|
||||||
|
* and, if you enable [file indexing](#file-indexing), all remaining folders will also get thumbnails (as long as they contain any pics at all)
|
||||||
|
|
||||||
in the grid/thumbnail view, if the audio player panel is open, songs will start playing when clicked
|
in the grid/thumbnail view, if the audio player panel is open, songs will start playing when clicked
|
||||||
* indicated by the audio files having the ▶ icon instead of 💾
|
* indicated by the audio files having the ▶ icon instead of 💾
|
||||||
@@ -929,14 +931,13 @@ through arguments:
|
|||||||
* `--xlink` enables deduplication across volumes
|
* `--xlink` enables deduplication across volumes
|
||||||
|
|
||||||
the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`, `d2ts`, `d2v` for disabling:
|
the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`, `d2ts`, `d2v` for disabling:
|
||||||
* `-v ~/music::r:c,e2dsa,e2tsr` does a full reindex of everything on startup
|
* `-v ~/music::r:c,e2ds,e2tsr` does a full reindex of everything on startup
|
||||||
* `-v ~/music::r:c,d2d` disables **all** indexing, even if any `-e2*` are on
|
* `-v ~/music::r:c,d2d` disables **all** indexing, even if any `-e2*` are on
|
||||||
* `-v ~/music::r:c,d2t` disables all `-e2t*` (tags), does not affect `-e2d*`
|
* `-v ~/music::r:c,d2t` disables all `-e2t*` (tags), does not affect `-e2d*`
|
||||||
* `-v ~/music::r:c,d2ds` disables on-boot scans; only index new uploads
|
* `-v ~/music::r:c,d2ds` disables on-boot scans; only index new uploads
|
||||||
* `-v ~/music::r:c,d2ts` same except only affecting tags
|
* `-v ~/music::r:c,d2ts` same except only affecting tags
|
||||||
|
|
||||||
note:
|
note:
|
||||||
* the parser can finally handle `c,e2dsa,e2tsr` so you no longer have to `c,e2dsa:c,e2tsr`
|
|
||||||
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
|
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
|
||||||
* the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
|
* the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
|
||||||
* deduplication is possible on windows if you run copyparty as administrator (not saying you should!)
|
* deduplication is possible on windows if you run copyparty as administrator (not saying you should!)
|
||||||
@@ -1158,9 +1159,33 @@ see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where
|
|||||||
|
|
||||||
## complete examples
|
## complete examples
|
||||||
|
|
||||||
* [running on windows](./docs/examples/windows.md)
|
* see [running on windows](./docs/examples/windows.md) for a fancy windows setup
|
||||||
|
|
||||||
* read-only music server
|
* or use any of the examples below, just replace `python copyparty-sfx.py` with `copyparty.exe` if you're using the exe edition
|
||||||
|
|
||||||
|
* allow anyone to download or upload files into the current folder:
|
||||||
|
`python copyparty-sfx.py`
|
||||||
|
|
||||||
|
* enable searching and music indexing with `-e2dsa -e2ts`
|
||||||
|
|
||||||
|
* start an FTP server on port 3921 with `--ftp 3921`
|
||||||
|
|
||||||
|
* announce it on your LAN with `-z` so it appears in windows/Linux file managers
|
||||||
|
|
||||||
|
* anyone can upload, but nobody can see any files (even the uploader):
|
||||||
|
`python copyparty-sfx.py -e2dsa -v .::w`
|
||||||
|
|
||||||
|
* block uploads if there's less than 4 GiB free disk space with `--df 4`
|
||||||
|
|
||||||
|
* show a popup on new uploads with `--xau bin/hooks/notify.py`
|
||||||
|
|
||||||
|
* anyone can upload, and receive "secret" links for each upload they do:
|
||||||
|
`python copyparty-sfx.py -e2dsa -v .::wG:c,fk=8`
|
||||||
|
|
||||||
|
* anyone can browse, only `kevin` (password `okgo`) can upload/move/delete files:
|
||||||
|
`python copyparty-sfx.py -e2dsa -a kevin:okgo -v .::r:rwmd,kevin`
|
||||||
|
|
||||||
|
* read-only music server:
|
||||||
`python copyparty-sfx.py -v /mnt/nas/music:/music:r -e2dsa -e2ts --no-robots --force-js --theme 2`
|
`python copyparty-sfx.py -v /mnt/nas/music:/music:r -e2dsa -e2ts --no-robots --force-js --theme 2`
|
||||||
|
|
||||||
* ...with bpm and key scanning
|
* ...with bpm and key scanning
|
||||||
@@ -1547,6 +1572,11 @@ both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#rev
|
|||||||
|
|
||||||
copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
|
copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
|
||||||
|
|
||||||
|
if [cfssl](https://github.com/cloudflare/cfssl/releases/latest) is installed, copyparty will automatically create a CA and server-cert on startup
|
||||||
|
* the certs are written to `--crt-dir` for distribution, see `--help` for the other `--crt` options
|
||||||
|
* this will be a self-signed certificate so you must install your `ca.pem` into all your browsers/devices
|
||||||
|
* if you want to avoid the hassle of distributing certs manually, please consider using a reverse proxy
|
||||||
|
|
||||||
|
|
||||||
# recovering from crashes
|
# recovering from crashes
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,44 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
cat >/dev/null <<'EOF'
|
||||||
|
|
||||||
|
NOTE: copyparty is now able to do this automatically;
|
||||||
|
however you may wish to use this script instead if
|
||||||
|
you have specific needs (or if copyparty breaks)
|
||||||
|
|
||||||
|
this script generates a new self-signed TLS certificate and
|
||||||
|
replaces the default insecure one that comes with copyparty
|
||||||
|
|
||||||
|
as it is trivial to impersonate a copyparty server using the
|
||||||
|
default certificate, it is highly recommended to do this
|
||||||
|
|
||||||
|
this will create a self-signed CA, and a Server certificate
|
||||||
|
which gets signed by that CA -- you can run it multiple times
|
||||||
|
with different server-FQDNs / IPs to create additional certs
|
||||||
|
for all your different servers / (non-)copyparty services
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
# ca-name and server-fqdn
|
# ca-name and server-fqdn
|
||||||
ca_name="$1"
|
ca_name="$1"
|
||||||
srv_fqdn="$2"
|
srv_fqdn="$2"
|
||||||
|
|
||||||
[ -z "$srv_fqdn" ] && {
|
[ -z "$srv_fqdn" ] && { cat <<'EOF'
|
||||||
echo "need arg 1: ca name"
|
need arg 1: ca name
|
||||||
echo "need arg 2: server fqdn and/or IPs, comma-separated"
|
need arg 2: server fqdn and/or IPs, comma-separated
|
||||||
echo "optional arg 3: if set, write cert into copyparty cfg"
|
optional arg 3: if set, write cert into copyparty cfg
|
||||||
|
|
||||||
|
example:
|
||||||
|
./cfssl.sh PartyCo partybox.local y
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
command -v cfssljson 2>/dev/null || {
|
||||||
|
echo please install cfssl and try again
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +89,14 @@ show() {
|
|||||||
}
|
}
|
||||||
show ca.pem
|
show ca.pem
|
||||||
show "$srv_fqdn.pem"
|
show "$srv_fqdn.pem"
|
||||||
|
echo
|
||||||
|
echo "successfully generated new certificates"
|
||||||
|
|
||||||
# write cert into copyparty config
|
# write cert into copyparty config
|
||||||
[ -z "$3" ] || {
|
[ -z "$3" ] || {
|
||||||
mkdir -p ~/.config/copyparty
|
mkdir -p ~/.config/copyparty
|
||||||
cat "$srv_fqdn".{key,pem} ca.pem >~/.config/copyparty/cert.pem
|
cat "$srv_fqdn".{key,pem} ca.pem >~/.config/copyparty/cert.pem
|
||||||
|
echo "successfully replaced copyparty certificate"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# Maintainer: icxes <dev.null@need.moe>
|
# Maintainer: icxes <dev.null@need.moe>
|
||||||
pkgname=copyparty
|
pkgname=copyparty
|
||||||
pkgver="1.7.0"
|
pkgver="1.7.4"
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Portable file sharing hub"
|
pkgdesc="Portable file sharing hub"
|
||||||
arch=("any")
|
arch=("any")
|
||||||
url="https://github.com/9001/${pkgname}"
|
url="https://github.com/9001/${pkgname}"
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
depends=("python" "lsof" "python-jinja")
|
depends=("python" "lsof" "python-jinja")
|
||||||
makedepends=("python-wheel" "python-build" "python-installer" "make" "pigz")
|
makedepends=("python-wheel" "python-setuptools" "python-build" "python-installer" "make" "pigz")
|
||||||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||||
"python-mutagen: music tags (alternative)"
|
"python-mutagen: music tags (alternative)"
|
||||||
"python-pillow: thumbnails for images"
|
"python-pillow: thumbnails for images"
|
||||||
@@ -19,7 +19,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||||||
)
|
)
|
||||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||||
backup=("etc/${pkgname}.d/init" )
|
backup=("etc/${pkgname}.d/init" )
|
||||||
sha256sums=("782e62eb1378e8d9d50af3fa1c18b95d11bb4721df93b4525beba80f14d55661")
|
sha256sums=("ae304043e806e3c39cd39c8919a514c2b2ec0f35b4cddb114eaf0e66b6826265")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"url": "https://github.com/9001/copyparty/releases/download/v1.7.0/copyparty-sfx.py",
|
"url": "https://github.com/9001/copyparty/releases/download/v1.7.4/copyparty-sfx.py",
|
||||||
"version": "1.7.0",
|
"version": "1.7.4",
|
||||||
"hash": "sha256-CW2+Bzt5bJyilCUCY0W0htV7J1LejDttMv2Mk5bFtgI="
|
"hash": "sha256-du7LYr28gzGy1zV2sTaUDHJfQ4dFFeyQS/TZCX42J5Q="
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# NOTE: this is now a built-in feature in copyparty
|
||||||
|
# but you may still want this if you have specific needs
|
||||||
|
#
|
||||||
# systemd service which generates a new TLS certificate on each boot,
|
# systemd service which generates a new TLS certificate on each boot,
|
||||||
# that way the one-year expiry time won't cause any issues --
|
# that way the one-year expiry time won't cause any issues --
|
||||||
# just have everyone trust the ca.pem once every 10 years
|
# just have everyone trust the ca.pem once every 10 years
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
# add '-i 127.0.0.1' to only allow local connections
|
# add '-i 127.0.0.1' to only allow local connections
|
||||||
# add '-e2dsa' to enable filesystem scanning + indexing
|
# add '-e2dsa' to enable filesystem scanning + indexing
|
||||||
# add '-e2ts' to enable metadata indexing
|
# add '-e2ts' to enable metadata indexing
|
||||||
|
# remove '--ansi' to disable colored logs
|
||||||
#
|
#
|
||||||
# with `Type=notify`, copyparty will signal systemd when it is ready to
|
# with `Type=notify`, copyparty will signal systemd when it is ready to
|
||||||
# accept connections; correctly delaying units depending on copyparty.
|
# accept connections; correctly delaying units depending on copyparty.
|
||||||
@@ -59,7 +60,7 @@ ExecStartPre=+nft add rule ip nat prerouting tcp dport 443 redirect to :3923
|
|||||||
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||||
|
|
||||||
# copyparty settings
|
# copyparty settings
|
||||||
ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py -e2d -v /mnt::rw
|
ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py --ansi -e2d -v /mnt::rw
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import platform
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
_:tuple[int,int]=(0,0) # _____________________________________________________________________ hey there! if you are reading this, your python is too old to run copyparty without some help. Please use https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py or the pypi package instead, or see https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#building if you want to build it yourself :-) ************************************************************************************************************************************************
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
except:
|
except:
|
||||||
@@ -27,7 +31,12 @@ WINDOWS: Any = (
|
|||||||
else False
|
else False
|
||||||
)
|
)
|
||||||
|
|
||||||
VT100 = not WINDOWS or WINDOWS >= [10, 0, 14393]
|
VT100 = "--ansi" in sys.argv or (
|
||||||
|
os.environ.get("NO_COLOR", "").lower() in ("", "0", "false")
|
||||||
|
and sys.stdout.isatty()
|
||||||
|
and "--no-ansi" not in sys.argv
|
||||||
|
and (not WINDOWS or WINDOWS >= [10, 0, 14393])
|
||||||
|
)
|
||||||
# introduced in anniversary update
|
# introduced in anniversary update
|
||||||
|
|
||||||
ANYWIN = WINDOWS or sys.platform in ["msys", "cygwin"]
|
ANYWIN = WINDOWS or sys.platform in ["msys", "cygwin"]
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ __url__ = "https://github.com/9001/copyparty/"
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import base64
|
import base64
|
||||||
import filecmp
|
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@@ -242,6 +240,24 @@ def get_srvname() -> str:
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_fk_salt(cert_path) -> str:
|
||||||
|
fp = os.path.join(E.cfg, "fk-salt.txt")
|
||||||
|
try:
|
||||||
|
with open(fp, "rb") as f:
|
||||||
|
ret = f.read().strip()
|
||||||
|
except:
|
||||||
|
if os.path.exists(cert_path):
|
||||||
|
zi = os.path.getmtime(cert_path)
|
||||||
|
ret = "{}".format(zi).encode("utf-8")
|
||||||
|
else:
|
||||||
|
ret = base64.b64encode(os.urandom(18))
|
||||||
|
|
||||||
|
with open(fp, "wb") as f:
|
||||||
|
f.write(ret + b"\n")
|
||||||
|
|
||||||
|
return ret.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
def ensure_locale() -> None:
|
def ensure_locale() -> None:
|
||||||
safe = "en_US.UTF-8"
|
safe = "en_US.UTF-8"
|
||||||
for x in [
|
for x in [
|
||||||
@@ -261,45 +277,22 @@ def ensure_locale() -> None:
|
|||||||
warn(t.format(safe))
|
warn(t.format(safe))
|
||||||
|
|
||||||
|
|
||||||
def ensure_cert(al: argparse.Namespace) -> None:
|
def ensure_webdeps() -> None:
|
||||||
|
ap = os.path.join(E.mod, "web/deps/mini-fa.woff")
|
||||||
|
if os.path.exists(ap):
|
||||||
|
return
|
||||||
|
|
||||||
|
warn(
|
||||||
|
"""could not find webdeps;
|
||||||
|
if you are running the sfx, or exe, or pypi package, or docker image,
|
||||||
|
then this is a bug! Please let me know so I can fix it, thanks :-)
|
||||||
|
https://github.com/9001/copyparty/issues/new?labels=bug&template=bug_report.md
|
||||||
|
|
||||||
|
however, if you are a dev, or running copyparty from source, and you want
|
||||||
|
full client functionality, you will need to build or obtain the webdeps:
|
||||||
|
https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#building
|
||||||
"""
|
"""
|
||||||
the default cert (and the entire TLS support) is only here to enable the
|
)
|
||||||
crypto.subtle javascript API, which is necessary due to the webkit guys
|
|
||||||
being massive memers (https://www.chromium.org/blink/webcrypto)
|
|
||||||
|
|
||||||
i feel awful about this and so should they
|
|
||||||
"""
|
|
||||||
cert_insec = os.path.join(E.mod, "res/insecure.pem")
|
|
||||||
cert_appdata = os.path.join(E.cfg, "cert.pem")
|
|
||||||
if not os.path.isfile(al.cert):
|
|
||||||
if cert_appdata != al.cert:
|
|
||||||
raise Exception("certificate file does not exist: " + al.cert)
|
|
||||||
|
|
||||||
shutil.copy(cert_insec, al.cert)
|
|
||||||
|
|
||||||
with open(al.cert, "rb") as f:
|
|
||||||
buf = f.read()
|
|
||||||
o1 = buf.find(b" PRIVATE KEY-")
|
|
||||||
o2 = buf.find(b" CERTIFICATE-")
|
|
||||||
m = "unsupported certificate format: "
|
|
||||||
if o1 < 0:
|
|
||||||
raise Exception(m + "no private key inside pem")
|
|
||||||
if o2 < 0:
|
|
||||||
raise Exception(m + "no server certificate inside pem")
|
|
||||||
if o1 > o2:
|
|
||||||
raise Exception(m + "private key must appear before server certificate")
|
|
||||||
|
|
||||||
try:
|
|
||||||
if filecmp.cmp(al.cert, cert_insec):
|
|
||||||
lprint(
|
|
||||||
"\033[33musing default TLS certificate; https will be insecure."
|
|
||||||
+ "\033[36m\ncertificate location: {}\033[0m\n".format(al.cert)
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# speaking of the default 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
|
|
||||||
|
|
||||||
|
|
||||||
def configure_ssl_ver(al: argparse.Namespace) -> None:
|
def configure_ssl_ver(al: argparse.Namespace) -> None:
|
||||||
@@ -729,6 +722,24 @@ def add_tls(ap, cert_path):
|
|||||||
ap2.add_argument("--ssl-log", metavar="PATH", type=u, help="log master secrets for later decryption in wireshark")
|
ap2.add_argument("--ssl-log", metavar="PATH", type=u, help="log master secrets for later decryption in wireshark")
|
||||||
|
|
||||||
|
|
||||||
|
def add_cert(ap, cert_path):
|
||||||
|
cert_dir = os.path.dirname(cert_path)
|
||||||
|
ap2 = ap.add_argument_group('TLS certificate generator options')
|
||||||
|
ap2.add_argument("--no-crt", action="store_true", help="disable automatic certificate creation")
|
||||||
|
ap2.add_argument("--crt-ns", metavar="N,N", type=u, default="", help="comma-separated list of FQDNs (domains) to add into the certificate")
|
||||||
|
ap2.add_argument("--crt-exact", action="store_true", help="do not add wildcard entries for each --crt-ns")
|
||||||
|
ap2.add_argument("--crt-noip", action="store_true", help="do not add autodetected IP addresses into cert")
|
||||||
|
ap2.add_argument("--crt-nolo", action="store_true", help="do not add 127.0.0.1 / localhost into cert")
|
||||||
|
ap2.add_argument("--crt-dir", metavar="PATH", default=cert_dir, help="where to save the CA cert")
|
||||||
|
ap2.add_argument("--crt-cdays", metavar="D", type=float, default=3650, help="ca-certificate expiration time in days")
|
||||||
|
ap2.add_argument("--crt-sdays", metavar="D", type=float, default=365, help="server-cert expiration time in days")
|
||||||
|
ap2.add_argument("--crt-cn", metavar="TXT", type=u, default="partyco", help="CA/server-cert common-name")
|
||||||
|
ap2.add_argument("--crt-cnc", metavar="TXT", type=u, default="--crt-cn", help="override CA name")
|
||||||
|
ap2.add_argument("--crt-cns", metavar="TXT", type=u, default="--crt-cn cpp", help="override server-cert name")
|
||||||
|
ap2.add_argument("--crt-back", metavar="HRS", type=float, default=72, help="backdate in hours")
|
||||||
|
ap2.add_argument("--crt-alg", metavar="S-N", type=u, default="ecdsa-256", help="algorithm and keysize; one of these: ecdsa-256 rsa-4096 rsa-2048")
|
||||||
|
|
||||||
|
|
||||||
def add_zeroconf(ap):
|
def add_zeroconf(ap):
|
||||||
ap2 = ap.add_argument_group("Zeroconf options")
|
ap2 = ap.add_argument_group("Zeroconf options")
|
||||||
ap2.add_argument("-z", action="store_true", help="enable all zeroconf backends (mdns, ssdp)")
|
ap2.add_argument("-z", action="store_true", help="enable all zeroconf backends (mdns, ssdp)")
|
||||||
@@ -870,6 +881,8 @@ def add_logging(ap):
|
|||||||
ap2 = ap.add_argument_group('logging options')
|
ap2 = ap.add_argument_group('logging options')
|
||||||
ap2.add_argument("-q", action="store_true", help="quiet")
|
ap2.add_argument("-q", action="store_true", help="quiet")
|
||||||
ap2.add_argument("-lo", metavar="PATH", type=u, help="logfile, example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz")
|
ap2.add_argument("-lo", metavar="PATH", type=u, help="logfile, example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz")
|
||||||
|
ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR")
|
||||||
|
ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR")
|
||||||
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
||||||
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
||||||
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
||||||
@@ -901,7 +914,7 @@ def add_thumbnail(ap):
|
|||||||
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown -- avoids doing keepalive pokes (updating the mtime) on thumbnail folders more often than SEC seconds")
|
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown -- avoids doing keepalive pokes (updating the mtime) on thumbnail folders more often than SEC seconds")
|
||||||
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled")
|
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled")
|
||||||
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age -- folders which haven't been poked for longer than --th-poke seconds will get deleted every --th-clean seconds")
|
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age -- folders which haven't been poked for longer than --th-poke seconds will get deleted every --th-clean seconds")
|
||||||
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; case-insensitive if -e2d")
|
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling -e2d will make these case-insensitive, and also automatically select thumbnails for all folders that contain pics, even if none match this pattern")
|
||||||
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
||||||
# https://github.com/libvips/libvips
|
# https://github.com/libvips/libvips
|
||||||
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
||||||
@@ -963,9 +976,11 @@ def add_db_metadata(ap):
|
|||||||
|
|
||||||
def add_ui(ap, retry):
|
def add_ui(ap, retry):
|
||||||
ap2 = ap.add_argument_group('ui options')
|
ap2 = ap.add_argument_group('ui options')
|
||||||
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
||||||
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language")
|
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language")
|
||||||
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use")
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use")
|
||||||
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
||||||
|
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching REGEX in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\.(js|css)$\033[0m] (volflag=unlist)")
|
||||||
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
||||||
ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
|
ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
|
||||||
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
||||||
@@ -1014,10 +1029,7 @@ def run_argparse(
|
|||||||
|
|
||||||
cert_path = os.path.join(E.cfg, "cert.pem")
|
cert_path = os.path.join(E.cfg, "cert.pem")
|
||||||
|
|
||||||
try:
|
fk_salt = get_fk_salt(cert_path)
|
||||||
fk_salt = unicode(os.path.getmtime(cert_path))
|
|
||||||
except:
|
|
||||||
fk_salt = "hunter2"
|
|
||||||
|
|
||||||
hcores = min(CORES, 4) # optimal on py3.11 @ r5-4500U
|
hcores = min(CORES, 4) # optimal on py3.11 @ r5-4500U
|
||||||
|
|
||||||
@@ -1028,6 +1040,7 @@ def run_argparse(
|
|||||||
add_general(ap, nc, srvname)
|
add_general(ap, nc, srvname)
|
||||||
add_network(ap)
|
add_network(ap)
|
||||||
add_tls(ap, cert_path)
|
add_tls(ap, cert_path)
|
||||||
|
add_cert(ap, cert_path)
|
||||||
add_qr(ap, tty)
|
add_qr(ap, tty)
|
||||||
add_zeroconf(ap)
|
add_zeroconf(ap)
|
||||||
add_zc_mdns(ap)
|
add_zc_mdns(ap)
|
||||||
@@ -1112,6 +1125,8 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||||||
|
|
||||||
ensure_locale()
|
ensure_locale()
|
||||||
|
|
||||||
|
ensure_webdeps()
|
||||||
|
|
||||||
for k, v in zip(argv[1:], argv[2:]):
|
for k, v in zip(argv[1:], argv[2:]):
|
||||||
if k == "-c" and os.path.isfile(v):
|
if k == "-c" and os.path.isfile(v):
|
||||||
supp = args_from_cfg(v)
|
supp = args_from_cfg(v)
|
||||||
@@ -1178,8 +1193,10 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||||||
except:
|
except:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if HAVE_SSL:
|
if al.ansi:
|
||||||
ensure_cert(al)
|
al.no_ansi = False
|
||||||
|
elif not al.no_ansi:
|
||||||
|
al.ansi = VT100
|
||||||
|
|
||||||
if WINDOWS and not al.keep_qem:
|
if WINDOWS and not al.keep_qem:
|
||||||
try:
|
try:
|
||||||
@@ -1187,7 +1204,7 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||||||
except:
|
except:
|
||||||
lprint("\nfailed to disable quick-edit-mode:\n" + min_ex() + "\n")
|
lprint("\nfailed to disable quick-edit-mode:\n" + min_ex() + "\n")
|
||||||
|
|
||||||
if not VT100:
|
if al.ansi:
|
||||||
al.wintitle = ""
|
al.wintitle = ""
|
||||||
|
|
||||||
nstrs: list[str] = []
|
nstrs: list[str] = []
|
||||||
@@ -1266,6 +1283,7 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||||||
configure_ssl_ciphers(al)
|
configure_ssl_ciphers(al)
|
||||||
else:
|
else:
|
||||||
warn("ssl module does not exist; cannot enable https")
|
warn("ssl module does not exist; cannot enable https")
|
||||||
|
al.http_only = True
|
||||||
|
|
||||||
if PY2 and WINDOWS and al.e2d:
|
if PY2 and WINDOWS and al.e2d:
|
||||||
warn(
|
warn(
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (1, 7, 1)
|
VERSION = (1, 7, 5)
|
||||||
CODENAME = "unlinked"
|
CODENAME = "unlinked"
|
||||||
BUILD_DT = (2023, 5, 7)
|
BUILD_DT = (2023, 6, 11)
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -841,6 +841,9 @@ class AuthSrv(object):
|
|||||||
if not ln.split("#")[0].strip():
|
if not ln.split("#")[0].strip():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if re.match(r"^\[.*\]:$", ln):
|
||||||
|
ln = ln[:-1]
|
||||||
|
|
||||||
subsection = ln in (catx, catf)
|
subsection = ln in (catx, catf)
|
||||||
if ln.startswith("[") or subsection:
|
if ln.startswith("[") or subsection:
|
||||||
self._e()
|
self._e()
|
||||||
@@ -1578,6 +1581,12 @@ class AuthSrv(object):
|
|||||||
if t:
|
if t:
|
||||||
self.log("\n\033[{}\033[0m\n".format(t))
|
self.log("\n\033[{}\033[0m\n".format(t))
|
||||||
|
|
||||||
|
zv, _ = vfs.get("/", "*", False, False)
|
||||||
|
zs = zv.realpath.lower()
|
||||||
|
if zs in ("/", "c:\\") or zs.startswith(r"c:\windows"):
|
||||||
|
t = "you are sharing a system directory: {}\n"
|
||||||
|
self.log(t.format(zv.realpath), c=1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zv, _ = vfs.get("/", "*", False, True)
|
zv, _ = vfs.get("/", "*", False, True)
|
||||||
if self.warn_anonwrite and os.getcwd() == zv.realpath:
|
if self.warn_anonwrite and os.getcwd() == zv.realpath:
|
||||||
|
|||||||
217
copyparty/cert.py
Normal file
217
copyparty/cert.py
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
import os
|
||||||
|
import errno
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import filecmp
|
||||||
|
import calendar
|
||||||
|
|
||||||
|
from .util import runcmd, Netdev
|
||||||
|
|
||||||
|
|
||||||
|
HAVE_CFSSL = True
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_cert(log: "RootLogger", args) -> None:
|
||||||
|
"""
|
||||||
|
the default cert (and the entire TLS support) is only here to enable the
|
||||||
|
crypto.subtle javascript API, which is necessary due to the webkit guys
|
||||||
|
being massive memers (https://www.chromium.org/blink/webcrypto)
|
||||||
|
|
||||||
|
i feel awful about this and so should they
|
||||||
|
"""
|
||||||
|
cert_insec = os.path.join(args.E.mod, "res/insecure.pem")
|
||||||
|
cert_appdata = os.path.join(args.E.cfg, "cert.pem")
|
||||||
|
if not os.path.isfile(args.cert):
|
||||||
|
if cert_appdata != args.cert:
|
||||||
|
raise Exception("certificate file does not exist: " + args.cert)
|
||||||
|
|
||||||
|
shutil.copy(cert_insec, args.cert)
|
||||||
|
|
||||||
|
with open(args.cert, "rb") as f:
|
||||||
|
buf = f.read()
|
||||||
|
o1 = buf.find(b" PRIVATE KEY-")
|
||||||
|
o2 = buf.find(b" CERTIFICATE-")
|
||||||
|
m = "unsupported certificate format: "
|
||||||
|
if o1 < 0:
|
||||||
|
raise Exception(m + "no private key inside pem")
|
||||||
|
if o2 < 0:
|
||||||
|
raise Exception(m + "no server certificate inside pem")
|
||||||
|
if o1 > o2:
|
||||||
|
raise Exception(m + "private key must appear before server certificate")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if filecmp.cmp(args.cert, cert_insec):
|
||||||
|
t = "using default TLS certificate; https will be insecure:\033[36m {}"
|
||||||
|
log("cert", t.format(args.cert), 3)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# speaking of the default 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
|
||||||
|
|
||||||
|
|
||||||
|
def _read_crt(args, fn):
|
||||||
|
try:
|
||||||
|
if not os.path.exists(os.path.join(args.crt_dir, fn)):
|
||||||
|
return 0, {}
|
||||||
|
|
||||||
|
acmd = ["cfssl-certinfo", "-cert", fn]
|
||||||
|
rc, so, se = runcmd(acmd, cwd=args.crt_dir)
|
||||||
|
if rc:
|
||||||
|
return 0, {}
|
||||||
|
|
||||||
|
inf = json.loads(so)
|
||||||
|
zs = inf["not_after"]
|
||||||
|
expiry = calendar.timegm(time.strptime(zs, "%Y-%m-%dT%H:%M:%SZ"))
|
||||||
|
return expiry, inf
|
||||||
|
except OSError as ex:
|
||||||
|
if ex.errno == errno.ENOENT:
|
||||||
|
raise
|
||||||
|
return 0, {}
|
||||||
|
except:
|
||||||
|
return 0, {}
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_ca(log: "RootLogger", args):
|
||||||
|
expiry = _read_crt(args, "ca.pem")[0]
|
||||||
|
if time.time() + args.crt_cdays * 60 * 60 * 24 * 0.1 < expiry:
|
||||||
|
return
|
||||||
|
|
||||||
|
backdate = "{}m".format(int(args.crt_back * 60))
|
||||||
|
expiry = "{}m".format(int(args.crt_cdays * 60 * 24))
|
||||||
|
cn = args.crt_cnc.replace("--crt-cn", args.crt_cn)
|
||||||
|
algo, ksz = args.crt_alg.split("-")
|
||||||
|
req = {
|
||||||
|
"CN": cn,
|
||||||
|
"CA": {"backdate": backdate, "expiry": expiry, "pathlen": 0},
|
||||||
|
"key": {"algo": algo, "size": int(ksz)},
|
||||||
|
"names": [{"O": cn}],
|
||||||
|
}
|
||||||
|
sin = json.dumps(req).encode("utf-8")
|
||||||
|
log("cert", "creating new ca ...", 6)
|
||||||
|
|
||||||
|
cmd = "cfssl gencert -initca -"
|
||||||
|
rc, so, se = runcmd(cmd.split(), 30, sin=sin)
|
||||||
|
if rc:
|
||||||
|
raise Exception("failed to create ca-cert: {}, {}".format(rc, se), 3)
|
||||||
|
|
||||||
|
cmd = "cfssljson -bare ca"
|
||||||
|
sin = so.encode("utf-8")
|
||||||
|
rc, so, se = runcmd(cmd.split(), 10, sin=sin, cwd=args.crt_dir)
|
||||||
|
if rc:
|
||||||
|
raise Exception("failed to translate ca-cert: {}, {}".format(rc, se), 3)
|
||||||
|
|
||||||
|
bname = os.path.join(args.crt_dir, "ca")
|
||||||
|
os.rename(bname + "-key.pem", bname + ".key")
|
||||||
|
os.unlink(bname + ".csr")
|
||||||
|
|
||||||
|
log("cert", "new ca OK", 2)
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_srv(log: "RootLogger", args, netdevs: dict[str, Netdev]):
|
||||||
|
names = args.crt_ns.split(",") if args.crt_ns else []
|
||||||
|
if not args.crt_exact:
|
||||||
|
for n in names[:]:
|
||||||
|
names.append("*.{}".format(n))
|
||||||
|
if not args.crt_noip:
|
||||||
|
for ip in netdevs.keys():
|
||||||
|
names.append(ip.split("/")[0])
|
||||||
|
if args.crt_nolo:
|
||||||
|
names = [x for x in names if x not in ("localhost", "127.0.0.1", "::1")]
|
||||||
|
if not names:
|
||||||
|
names = ["127.0.0.1"]
|
||||||
|
if "127.0.0.1" in names or "::1" in names:
|
||||||
|
names.append("localhost")
|
||||||
|
names = list({x: 1 for x in names}.keys())
|
||||||
|
|
||||||
|
try:
|
||||||
|
expiry, inf = _read_crt(args, "srv.pem")
|
||||||
|
expired = time.time() + args.crt_sdays * 60 * 60 * 24 * 0.1 > expiry
|
||||||
|
cert_insec = os.path.join(args.E.mod, "res/insecure.pem")
|
||||||
|
for n in names:
|
||||||
|
if n not in inf["sans"]:
|
||||||
|
raise Exception("does not have {}".format(n))
|
||||||
|
if expired:
|
||||||
|
raise Exception("old server-cert has expired")
|
||||||
|
if not filecmp.cmp(args.cert, cert_insec):
|
||||||
|
return
|
||||||
|
except Exception as ex:
|
||||||
|
log("cert", "will create new server-cert; {}".format(ex))
|
||||||
|
|
||||||
|
log("cert", "creating server-cert ...", 6)
|
||||||
|
|
||||||
|
backdate = "{}m".format(int(args.crt_back * 60))
|
||||||
|
expiry = "{}m".format(int(args.crt_sdays * 60 * 24))
|
||||||
|
cfg = {
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"backdate": backdate,
|
||||||
|
"expiry": expiry,
|
||||||
|
"usages": ["signing", "key encipherment", "server auth"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with open(os.path.join(args.crt_dir, "cfssl.json"), "wb") as f:
|
||||||
|
f.write(json.dumps(cfg).encode("utf-8"))
|
||||||
|
|
||||||
|
cn = args.crt_cns.replace("--crt-cn", args.crt_cn)
|
||||||
|
algo, ksz = args.crt_alg.split("-")
|
||||||
|
req = {
|
||||||
|
"key": {"algo": algo, "size": int(ksz)},
|
||||||
|
"names": [{"O": cn}],
|
||||||
|
}
|
||||||
|
sin = json.dumps(req).encode("utf-8")
|
||||||
|
|
||||||
|
cmd = "cfssl gencert -config=cfssl.json -ca ca.pem -ca-key ca.key -profile=www"
|
||||||
|
acmd = cmd.split() + ["-hostname=" + ",".join(names), "-"]
|
||||||
|
rc, so, se = runcmd(acmd, 30, sin=sin, cwd=args.crt_dir)
|
||||||
|
if rc:
|
||||||
|
raise Exception("failed to create cert: {}, {}".format(rc, se))
|
||||||
|
|
||||||
|
cmd = "cfssljson -bare srv"
|
||||||
|
sin = so.encode("utf-8")
|
||||||
|
rc, so, se = runcmd(cmd.split(), 10, sin=sin, cwd=args.crt_dir)
|
||||||
|
if rc:
|
||||||
|
raise Exception("failed to translate cert: {}, {}".format(rc, se))
|
||||||
|
|
||||||
|
bname = os.path.join(args.crt_dir, "srv")
|
||||||
|
os.rename(bname + "-key.pem", bname + ".key")
|
||||||
|
os.unlink(bname + ".csr")
|
||||||
|
|
||||||
|
with open(os.path.join(args.crt_dir, "ca.pem"), "rb") as f:
|
||||||
|
ca = f.read()
|
||||||
|
|
||||||
|
with open(bname + ".key", "rb") as f:
|
||||||
|
skey = f.read()
|
||||||
|
|
||||||
|
with open(bname + ".pem", "rb") as f:
|
||||||
|
scrt = f.read()
|
||||||
|
|
||||||
|
with open(args.cert, "wb") as f:
|
||||||
|
f.write(skey + scrt + ca)
|
||||||
|
|
||||||
|
log("cert", "new server-cert OK", 2)
|
||||||
|
|
||||||
|
|
||||||
|
def gencert(log: "RootLogger", args, netdevs: dict[str, Netdev]):
|
||||||
|
global HAVE_CFSSL
|
||||||
|
|
||||||
|
if args.http_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
if args.no_crt or not HAVE_CFSSL:
|
||||||
|
ensure_cert(log, args)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
_gen_ca(log, args)
|
||||||
|
_gen_srv(log, args, netdevs)
|
||||||
|
except Exception as ex:
|
||||||
|
HAVE_CFSSL = False
|
||||||
|
log("cert", "could not create TLS certificates: {}".format(ex), 3)
|
||||||
|
if getattr(ex, "errno", 0) == errno.ENOENT:
|
||||||
|
t = "install cfssl if you want to fix this; https://github.com/cloudflare/cfssl/releases/latest"
|
||||||
|
log("cert", t, 6)
|
||||||
|
|
||||||
|
ensure_cert(log, args)
|
||||||
@@ -24,6 +24,7 @@ def vf_bmap() -> dict[str, str]:
|
|||||||
"e2v",
|
"e2v",
|
||||||
"e2vu",
|
"e2vu",
|
||||||
"e2vp",
|
"e2vp",
|
||||||
|
"grid",
|
||||||
"hardlink",
|
"hardlink",
|
||||||
"magic",
|
"magic",
|
||||||
"no_sb_md",
|
"no_sb_md",
|
||||||
@@ -40,7 +41,7 @@ def vf_bmap() -> dict[str, str]:
|
|||||||
def vf_vmap() -> dict[str, str]:
|
def vf_vmap() -> dict[str, str]:
|
||||||
"""argv-to-volflag: simple values"""
|
"""argv-to-volflag: simple values"""
|
||||||
ret = {}
|
ret = {}
|
||||||
for k in ("lg_sbf", "md_sbf"):
|
for k in ("lg_sbf", "md_sbf", "unlist"):
|
||||||
ret[k] = k
|
ret[k] = k
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -133,6 +134,8 @@ flagcats = {
|
|||||||
"xm=CMD": "execute CMD on message",
|
"xm=CMD": "execute CMD on message",
|
||||||
},
|
},
|
||||||
"client and ux": {
|
"client and ux": {
|
||||||
|
"grid": "show grid/thumbnails by default",
|
||||||
|
"unlist": "dont list files matching REGEX",
|
||||||
"html_head=TXT": "includes TXT in the <head>",
|
"html_head=TXT": "includes TXT in the <head>",
|
||||||
"robots": "allows indexing by search engines (default)",
|
"robots": "allows indexing by search engines (default)",
|
||||||
"norobots": "kindly asks search engines to leave",
|
"norobots": "kindly asks search engines to leave",
|
||||||
|
|||||||
@@ -502,9 +502,9 @@ class Ftpd(object):
|
|||||||
|
|
||||||
for h_lp in hs:
|
for h_lp in hs:
|
||||||
h2, lp = h_lp
|
h2, lp = h_lp
|
||||||
h2.hub = hub
|
FtpHandler.hub = h2.hub = hub
|
||||||
h2.args = hub.args
|
FtpHandler.args = h2.args = hub.args
|
||||||
h2.authorizer = FtpAuth(hub)
|
FtpHandler.authorizer = h2.authorizer = FtpAuth(hub)
|
||||||
|
|
||||||
if self.args.ftp_pr:
|
if self.args.ftp_pr:
|
||||||
p1, p2 = [int(x) for x in self.args.ftp_pr.split("-")]
|
p1, p2 = [int(x) for x in self.args.ftp_pr.split("-")]
|
||||||
|
|||||||
@@ -726,7 +726,7 @@ class HttpCli(object):
|
|||||||
|
|
||||||
def handle_get(self) -> bool:
|
def handle_get(self) -> bool:
|
||||||
if self.do_log:
|
if self.do_log:
|
||||||
logmsg = "%4s %s @%s" % (self.mode, self.req, self.uname)
|
logmsg = "%-4s %s @%s" % (self.mode, self.req, self.uname)
|
||||||
|
|
||||||
if "range" in self.headers:
|
if "range" in self.headers:
|
||||||
try:
|
try:
|
||||||
@@ -3459,6 +3459,7 @@ class HttpCli(object):
|
|||||||
break
|
break
|
||||||
|
|
||||||
vf = vn.flags
|
vf = vn.flags
|
||||||
|
unlist = vf.get("unlist", "")
|
||||||
ls_ret = {
|
ls_ret = {
|
||||||
"dirs": [],
|
"dirs": [],
|
||||||
"files": [],
|
"files": [],
|
||||||
@@ -3469,6 +3470,7 @@ class HttpCli(object):
|
|||||||
"itag": e2t,
|
"itag": e2t,
|
||||||
"lifetime": vn.flags.get("lifetime") or 0,
|
"lifetime": vn.flags.get("lifetime") or 0,
|
||||||
"frand": bool(vn.flags.get("rand")),
|
"frand": bool(vn.flags.get("rand")),
|
||||||
|
"unlist": unlist,
|
||||||
"perms": perms,
|
"perms": perms,
|
||||||
"logues": logues,
|
"logues": logues,
|
||||||
"readme": readme,
|
"readme": readme,
|
||||||
@@ -3500,6 +3502,8 @@ class HttpCli(object):
|
|||||||
"readme": readme,
|
"readme": readme,
|
||||||
"title": html_escape(self.vpath, crlf=True) or "💾🎉",
|
"title": html_escape(self.vpath, crlf=True) or "💾🎉",
|
||||||
"srv_info": srv_infot,
|
"srv_info": srv_infot,
|
||||||
|
"dgrid": "grid" in vf,
|
||||||
|
"unlist": unlist,
|
||||||
"dtheme": self.args.theme,
|
"dtheme": self.args.theme,
|
||||||
"themes": self.args.themes,
|
"themes": self.args.themes,
|
||||||
"turbolvl": self.args.turbo,
|
"turbolvl": self.args.turbo,
|
||||||
@@ -3729,7 +3733,12 @@ class HttpCli(object):
|
|||||||
dirs.sort(key=itemgetter("name"))
|
dirs.sort(key=itemgetter("name"))
|
||||||
|
|
||||||
if is_js:
|
if is_js:
|
||||||
j2a["ls0"] = {"dirs": dirs, "files": files, "taglist": taglist}
|
j2a["ls0"] = {
|
||||||
|
"dirs": dirs,
|
||||||
|
"files": files,
|
||||||
|
"taglist": taglist,
|
||||||
|
"unlist": unlist,
|
||||||
|
}
|
||||||
j2a["files"] = []
|
j2a["files"] = []
|
||||||
else:
|
else:
|
||||||
j2a["files"] = dirs + files
|
j2a["files"] = dirs + files
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ class HttpConn(object):
|
|||||||
self.args: argparse.Namespace = hsrv.args # mypy404
|
self.args: argparse.Namespace = hsrv.args # mypy404
|
||||||
self.E: EnvParams = self.args.E
|
self.E: EnvParams = self.args.E
|
||||||
self.asrv: AuthSrv = hsrv.asrv # mypy404
|
self.asrv: AuthSrv = hsrv.asrv # mypy404
|
||||||
self.cert_path = hsrv.cert_path
|
|
||||||
self.u2fh: Util.FHC = hsrv.u2fh # mypy404
|
self.u2fh: Util.FHC = hsrv.u2fh # mypy404
|
||||||
self.iphash: HMaccas = hsrv.broker.iphash
|
self.iphash: HMaccas = hsrv.broker.iphash
|
||||||
self.bans: dict[str, int] = hsrv.bans
|
self.bans: dict[str, int] = hsrv.bans
|
||||||
@@ -114,7 +113,7 @@ class HttpConn(object):
|
|||||||
|
|
||||||
def _detect_https(self) -> bool:
|
def _detect_https(self) -> bool:
|
||||||
method = None
|
method = None
|
||||||
if self.cert_path:
|
if True:
|
||||||
try:
|
try:
|
||||||
method = self.s.recv(4, socket.MSG_PEEK)
|
method = self.s.recv(4, socket.MSG_PEEK)
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
@@ -148,7 +147,7 @@ class HttpConn(object):
|
|||||||
self.sr = None
|
self.sr = None
|
||||||
if self.args.https_only:
|
if self.args.https_only:
|
||||||
is_https = True
|
is_https = True
|
||||||
elif self.args.http_only or not HAVE_SSL:
|
elif self.args.http_only:
|
||||||
is_https = False
|
is_https = False
|
||||||
else:
|
else:
|
||||||
# raise Exception("asdf")
|
# raise Exception("asdf")
|
||||||
@@ -162,7 +161,7 @@ class HttpConn(object):
|
|||||||
self.log_src = self.log_src.replace("[36m", "[35m")
|
self.log_src = self.log_src.replace("[36m", "[35m")
|
||||||
try:
|
try:
|
||||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
ctx.load_cert_chain(self.cert_path)
|
ctx.load_cert_chain(self.args.cert)
|
||||||
if self.args.ssl_ver:
|
if self.args.ssl_ver:
|
||||||
ctx.options &= ~self.args.ssl_flags_en
|
ctx.options &= ~self.args.ssl_flags_en
|
||||||
ctx.options |= self.args.ssl_flags_de
|
ctx.options |= self.args.ssl_flags_de
|
||||||
|
|||||||
@@ -33,7 +33,23 @@ except MNFE:
|
|||||||
* (try another python version, if you have one)
|
* (try another python version, if you have one)
|
||||||
* (try copyparty.sfx instead)
|
* (try copyparty.sfx instead)
|
||||||
""".format(
|
""".format(
|
||||||
os.path.basename(sys.executable)
|
sys.executable
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
except SyntaxError:
|
||||||
|
if EXE:
|
||||||
|
raise
|
||||||
|
|
||||||
|
print(
|
||||||
|
"""\033[1;31m
|
||||||
|
your jinja2 version is incompatible with your python version;\033[33m
|
||||||
|
please try to replace it with an older version:\033[0m
|
||||||
|
* {} -m pip install --user jinja2==2.11.3
|
||||||
|
* (try another python version, if you have one)
|
||||||
|
* (try copyparty.sfx instead)
|
||||||
|
""".format(
|
||||||
|
sys.executable
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -132,12 +148,6 @@ class HttpSrv(object):
|
|||||||
|
|
||||||
self.ssdp = SSDPr(broker)
|
self.ssdp = SSDPr(broker)
|
||||||
|
|
||||||
cert_path = self.args.cert
|
|
||||||
if bos.path.exists(cert_path):
|
|
||||||
self.cert_path = cert_path
|
|
||||||
else:
|
|
||||||
self.cert_path = ""
|
|
||||||
|
|
||||||
if self.tp_q:
|
if self.tp_q:
|
||||||
self.start_threads(4)
|
self.start_threads(4)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
|
import errno
|
||||||
import random
|
import random
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
@@ -277,6 +278,18 @@ class MDNS(MCast):
|
|||||||
zf = time.time() + 2
|
zf = time.time() + 2
|
||||||
self.probing = zf # cant unicast so give everyone an extra sec
|
self.probing = zf # cant unicast so give everyone an extra sec
|
||||||
self.unsolicited = [zf, zf + 1, zf + 3, zf + 7] # rfc-8.3
|
self.unsolicited = [zf, zf + 1, zf + 3, zf + 7] # rfc-8.3
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.run2()
|
||||||
|
except OSError as ex:
|
||||||
|
if ex.errno != errno.EBADF:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.log("stopping due to {}".format(ex), "90")
|
||||||
|
|
||||||
|
self.log("stopped", 2)
|
||||||
|
|
||||||
|
def run2(self) -> None:
|
||||||
last_hop = time.time()
|
last_hop = time.time()
|
||||||
ihop = self.args.mc_hop
|
ihop = self.args.mc_hop
|
||||||
while self.running:
|
while self.running:
|
||||||
@@ -314,8 +327,6 @@ class MDNS(MCast):
|
|||||||
self.log(t.format(self.hn[:-1]), 2)
|
self.log(t.format(self.hn[:-1]), 2)
|
||||||
self.probing = 0
|
self.probing = 0
|
||||||
|
|
||||||
self.log("stopped", 2)
|
|
||||||
|
|
||||||
def stop(self, panic=False) -> None:
|
def stop(self, panic=False) -> None:
|
||||||
self.running = False
|
self.running = False
|
||||||
for srv in self.srv.values():
|
for srv in self.srv.values():
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
|
import errno
|
||||||
import re
|
import re
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
@@ -129,6 +130,17 @@ class SSDPd(MCast):
|
|||||||
srv.hport = hp
|
srv.hport = hp
|
||||||
|
|
||||||
self.log("listening")
|
self.log("listening")
|
||||||
|
try:
|
||||||
|
self.run2()
|
||||||
|
except OSError as ex:
|
||||||
|
if ex.errno != errno.EBADF:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.log("stopping due to {}".format(ex), "90")
|
||||||
|
|
||||||
|
self.log("stopped", 2)
|
||||||
|
|
||||||
|
def run2(self) -> None:
|
||||||
while self.running:
|
while self.running:
|
||||||
rdy = select.select(self.srv, [], [], self.args.z_chk or 180)
|
rdy = select.select(self.srv, [], [], self.args.z_chk or 180)
|
||||||
rx: list[socket.socket] = rdy[0] # type: ignore
|
rx: list[socket.socket] = rdy[0] # type: ignore
|
||||||
@@ -148,8 +160,6 @@ class SSDPd(MCast):
|
|||||||
)
|
)
|
||||||
self.log(t, 6)
|
self.log(t, 6)
|
||||||
|
|
||||||
self.log("stopped", 2)
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
self.running = False
|
self.running = False
|
||||||
for srv in self.srv.values():
|
for srv in self.srv.values():
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if True: # pylint: disable=using-constant-test
|
|||||||
import typing
|
import typing
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from .__init__ import ANYWIN, EXE, MACOS, TYPE_CHECKING, VT100, EnvParams, unicode
|
from .__init__ import ANYWIN, EXE, MACOS, TYPE_CHECKING, EnvParams, unicode
|
||||||
from .authsrv import AuthSrv
|
from .authsrv import AuthSrv
|
||||||
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE
|
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE
|
||||||
from .tcpsrv import TcpSrv
|
from .tcpsrv import TcpSrv
|
||||||
@@ -80,6 +80,7 @@ class SvcHub(object):
|
|||||||
self.dargs = dargs
|
self.dargs = dargs
|
||||||
self.argv = argv
|
self.argv = argv
|
||||||
self.E: EnvParams = args.E
|
self.E: EnvParams = args.E
|
||||||
|
self.no_ansi = args.no_ansi
|
||||||
self.logf: Optional[typing.TextIO] = None
|
self.logf: Optional[typing.TextIO] = None
|
||||||
self.logf_base_fn = ""
|
self.logf_base_fn = ""
|
||||||
self.stop_req = False
|
self.stop_req = False
|
||||||
@@ -681,11 +682,15 @@ class SvcHub(object):
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
if now >= self.next_day:
|
if now >= self.next_day:
|
||||||
dt = datetime.utcfromtimestamp(now)
|
dt = datetime.utcfromtimestamp(now)
|
||||||
print("\033[36m{}\033[0m\n".format(dt.strftime("%Y-%m-%d")), end="")
|
zs = "{}\n" if self.no_ansi else "\033[36m{}\033[0m\n"
|
||||||
|
zs = zs.format(dt.strftime("%Y-%m-%d"))
|
||||||
|
print(zs, end="")
|
||||||
self._set_next_day()
|
self._set_next_day()
|
||||||
|
if self.logf:
|
||||||
|
self.logf.write(zs)
|
||||||
|
|
||||||
fmt = "\033[36m%s \033[33m%-21s \033[0m%s\n"
|
fmt = "\033[36m%s \033[33m%-21s \033[0m%s\n"
|
||||||
if not VT100:
|
if self.no_ansi:
|
||||||
fmt = "%s %-21s %s\n"
|
fmt = "%s %-21s %s\n"
|
||||||
if "\033" in msg:
|
if "\033" in msg:
|
||||||
msg = ansi_re.sub("", msg)
|
msg = ansi_re.sub("", msg)
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import socket
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, VT100, unicode
|
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
|
||||||
from .stolen.qrcodegen import QrCode
|
from .stolen.qrcodegen import QrCode
|
||||||
|
from .cert import gencert
|
||||||
from .util import (
|
from .util import (
|
||||||
E_ACCESS,
|
E_ACCESS,
|
||||||
E_ADDR_IN_USE,
|
E_ADDR_IN_USE,
|
||||||
@@ -295,6 +296,7 @@ class TcpSrv(object):
|
|||||||
def _distribute_netdevs(self):
|
def _distribute_netdevs(self):
|
||||||
self.hub.broker.say("set_netdevs", self.netdevs)
|
self.hub.broker.say("set_netdevs", self.netdevs)
|
||||||
self.hub.start_zeroconf()
|
self.hub.start_zeroconf()
|
||||||
|
gencert(self.log, self.args, self.netdevs)
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
self.stopping = True
|
self.stopping = True
|
||||||
@@ -501,7 +503,7 @@ class TcpSrv(object):
|
|||||||
zoom = 1
|
zoom = 1
|
||||||
|
|
||||||
qr = qrc.render(zoom, pad)
|
qr = qrc.render(zoom, pad)
|
||||||
if not VT100:
|
if self.args.no_ansi:
|
||||||
return "{}\n{}".format(txt, qr)
|
return "{}\n{}".format(txt, qr)
|
||||||
|
|
||||||
halfc = "\033[40;48;5;{0}m{1}\033[47;48;5;{2}m"
|
halfc = "\033[40;48;5;{0}m{1}\033[47;48;5;{2}m"
|
||||||
|
|||||||
@@ -2564,7 +2564,7 @@ class Up2k(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if "hardlink" in flags:
|
if "hardlink" in flags:
|
||||||
os.link(fsenc(src), fsenc(dst))
|
os.link(fsenc(absreal(src)), fsenc(dst))
|
||||||
linked = True
|
linked = True
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log("cannot hardlink: " + repr(ex))
|
self.log("cannot hardlink: " + repr(ex))
|
||||||
|
|||||||
@@ -2019,6 +2019,8 @@ def shut_socket(log: "NamedLogger", sck: socket.socket, timeout: int = 3) -> Non
|
|||||||
sck.shutdown(socket.SHUT_RDWR)
|
sck.shutdown(socket.SHUT_RDWR)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
except Exception as ex:
|
||||||
|
log("shut({}): {}".format(fd, ex), "90")
|
||||||
finally:
|
finally:
|
||||||
td = time.time() - t0
|
td = time.time() - t0
|
||||||
if td >= 1:
|
if td >= 1:
|
||||||
|
|||||||
@@ -1751,6 +1751,7 @@ html.y #tree.nowrap .ntree a+a:hover {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.ghead {
|
.ghead {
|
||||||
|
background: var(--bg-u2);
|
||||||
border-radius: .3em;
|
border-radius: .3em;
|
||||||
padding: .2em .5em;
|
padding: .2em .5em;
|
||||||
line-height: 2.3em;
|
line-height: 2.3em;
|
||||||
@@ -2947,6 +2948,7 @@ html.b #treepar {
|
|||||||
html.b #wrap {
|
html.b #wrap {
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
}
|
}
|
||||||
|
html.by .ghead,
|
||||||
html.bz .ghead {
|
html.bz .ghead {
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
padding: .2em 0;
|
padding: .2em 0;
|
||||||
|
|||||||
@@ -138,6 +138,7 @@
|
|||||||
TS = "{{ ts }}",
|
TS = "{{ ts }}",
|
||||||
acct = "{{ acct }}",
|
acct = "{{ acct }}",
|
||||||
perms = {{ perms }},
|
perms = {{ perms }},
|
||||||
|
dgrid = {{ dgrid|tojson }},
|
||||||
themes = {{ themes }},
|
themes = {{ themes }},
|
||||||
dtheme = "{{ dtheme }}",
|
dtheme = "{{ dtheme }}",
|
||||||
srvinf = "{{ srv_info }}",
|
srvinf = "{{ srv_info }}",
|
||||||
|
|||||||
@@ -1974,7 +1974,10 @@ var pbar = (function () {
|
|||||||
w = 8,
|
w = 8,
|
||||||
apos, adur;
|
apos, adur;
|
||||||
|
|
||||||
clearTimeout(t_redraw);
|
if (t_redraw) {
|
||||||
|
clearTimeout(t_redraw);
|
||||||
|
t_redraw = 0;
|
||||||
|
}
|
||||||
pctx.clearRect(0, 0, pc.w, pc.h);
|
pctx.clearRect(0, 0, pc.w, pc.h);
|
||||||
|
|
||||||
if (!mp || !mp.au || !isNum(adur = mp.au.duration) || !isNum(apos = mp.au.currentTime) || apos < 0 || adur < apos)
|
if (!mp || !mp.au || !isNum(adur = mp.au.duration) || !isNum(apos = mp.au.currentTime) || apos < 0 || adur < apos)
|
||||||
@@ -4523,7 +4526,7 @@ var thegrid = (function () {
|
|||||||
|
|
||||||
bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty);
|
bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty);
|
||||||
bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel);
|
bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel);
|
||||||
bcfg_bind(r, 'en', 'griden', false, function (v) {
|
bcfg_bind(r, 'en', 'griden', dgrid, function (v) {
|
||||||
v ? loadgrid() : r.setvis(true);
|
v ? loadgrid() : r.setvis(true);
|
||||||
pbar.onresize();
|
pbar.onresize();
|
||||||
vbar.onresize();
|
vbar.onresize();
|
||||||
@@ -5723,6 +5726,12 @@ var treectl = (function () {
|
|||||||
seen = {};
|
seen = {};
|
||||||
|
|
||||||
r.lsc = res;
|
r.lsc = res;
|
||||||
|
if (res.unlist) {
|
||||||
|
var ptn = new RegExp(res.unlist);
|
||||||
|
for (var a = nodes.length - 1; a >= 0; a--)
|
||||||
|
if (ptn.exec(nodes[a].href.split('?')[0]))
|
||||||
|
nodes.splice(a, 1);
|
||||||
|
}
|
||||||
nodes = sortfiles(nodes);
|
nodes = sortfiles(nodes);
|
||||||
window.removeEventListener('scroll', r.tscroll);
|
window.removeEventListener('scroll', r.tscroll);
|
||||||
r.trunc = nodes.length > r.nvis && location.hash.length < 2;
|
r.trunc = nodes.length > r.nvis && location.hash.length < 2;
|
||||||
|
|||||||
@@ -1826,6 +1826,7 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
timer.rm(etafun);
|
timer.rm(etafun);
|
||||||
timer.rm(donut.do);
|
timer.rm(donut.do);
|
||||||
|
ebi('u2tabw').style.minHeight = '0px';
|
||||||
utw_minh = 0;
|
utw_minh = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||||||
var html = [
|
var html = [
|
||||||
'<h1>you hit a bug!</h1>',
|
'<h1>you hit a bug!</h1>',
|
||||||
'<p style="font-size:1.3em;margin:0">try to <a href="#" onclick="localStorage.clear();location.reload();">reset copyparty settings</a> if you are stuck here, or <a href="#" onclick="ignex();">ignore this</a> / <a href="#" onclick="ignex(true);">ignore all</a> / <a href="?b=u">basic</a></p>',
|
'<p style="font-size:1.3em;margin:0">try to <a href="#" onclick="localStorage.clear();location.reload();">reset copyparty settings</a> if you are stuck here, or <a href="#" onclick="ignex();">ignore this</a> / <a href="#" onclick="ignex(true);">ignore all</a> / <a href="?b=u">basic</a></p>',
|
||||||
'<p style="color:#fff">please send me a screenshot arigathanks gozaimuch: <a href="<ghi>" target="_blank">github issue</a> or <code>ed#2644</code></p>',
|
'<p style="color:#fff">please send me a screenshot arigathanks gozaimuch: <a href="<ghi>" target="_blank">github issue</a></p>',
|
||||||
'<p class="b">' + esc(url + ' @' + lineNo + ':' + columnNo), '<br />' + esc(String(msg)).replace(/\n/g, '<br />') + '</p>',
|
'<p class="b">' + esc(url + ' @' + lineNo + ':' + columnNo), '<br />' + esc(String(msg)).replace(/\n/g, '<br />') + '</p>',
|
||||||
'<p><b>UA:</b> ' + esc(navigator.userAgent + '')
|
'<p><b>UA:</b> ' + esc(navigator.userAgent + '')
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,3 +1,50 @@
|
|||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
|
# 2023-0513-0000 `v1.7.2` hard resolve
|
||||||
|
|
||||||
|
## new features
|
||||||
|
* print a warning if `c:\`, `c:\windows*`, or all of `/` are shared
|
||||||
|
* upgraded the docker image to v3.18 which enables the [chiptune player](https://a.ocv.me/pub/demo/music/chiptunes/#af-f6fb2e5f)
|
||||||
|
* in config files, allow trailing `:` in section headers
|
||||||
|
|
||||||
|
## bugfixes
|
||||||
|
* when `--hardlink` (or the volflag) is set, resolve symlinks before hardlinking
|
||||||
|
* uploads could fail due to relative symlinks
|
||||||
|
* really minor ux fixes
|
||||||
|
* left-align `GET` in access logs
|
||||||
|
* the upload panel didn't always shrink back down after uploads completed
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
|
# 2023-0507-1834 `v1.7.1` CräzY;PWDs
|
||||||
|
|
||||||
|
## new features
|
||||||
|
* webdav:
|
||||||
|
* support write-only folders
|
||||||
|
* option `--dav-auth` / volflag `davauth` forces clients to always auth
|
||||||
|
* helps clients such as `davfs2` see all folders if the root is anon-readable but some subfolders are not
|
||||||
|
* alternatively you could configure your client to always send the password in the `PW` header
|
||||||
|
* include usernames in http request logs
|
||||||
|
* audio player:
|
||||||
|
* consumes less power on phones when the screen is off
|
||||||
|
* smoother playback cursor on short songs
|
||||||
|
|
||||||
|
## bugfixes
|
||||||
|
* the characters `;` and `%` can now be used in passwords
|
||||||
|
* but non-ascii characters (such as the ä in the release title) can, in fact, not
|
||||||
|
* verify that all accounts have unique passwords on startup (#25)
|
||||||
|
|
||||||
|
## other changes
|
||||||
|
* ftpd: log incorrect passwords only, not correct ones
|
||||||
|
* `up2k.py` (the upload, folder-sync, and file-search client) has been renamed to [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
|
||||||
|
* `u2c` as in `up2k client`, or `up2k CLI`, or `upload-to-copyparty` -- good name
|
||||||
|
* now the only things named "up2k" are the web-ui and the server backend which is way less confusing
|
||||||
|
* upgrade packaging from [setup.py](https://github.com/9001/copyparty/blob/hovudstraum/setup.py) to [pyproject.toml](https://github.com/9001/copyparty/blob/hovudstraum/pyproject.toml)
|
||||||
|
* no practical consequences aside from a warm fuzzy feeling of being in the future
|
||||||
|
* the docker images ~~will be~~ got rebuilt 2023-05-11 ~~in a few days (when [alpine](https://alpinelinux.org/) 3.18 is released)~~ enabling [the chiptune player](https://a.ocv.me/pub/demo/music/chiptunes/#af-f6fb2e5f)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
# 2023-0429-2114 `v1.7.0` unlinked
|
# 2023-0429-2114 `v1.7.0` unlinked
|
||||||
|
|
||||||
|
|||||||
@@ -236,25 +236,13 @@ pip install black==21.12b0 click==8.0.2 bandit pylint flake8 isort mypy # vscod
|
|||||||
|
|
||||||
## just the sfx
|
## just the sfx
|
||||||
|
|
||||||
first grab the web-dependencies from a previous sfx (assuming you don't need to modify something in those):
|
if you just want to modify the copyparty source code (py/html/css/js) then this is the easiest approach
|
||||||
|
|
||||||
```sh
|
build the sfx using any of the following examples:
|
||||||
rm -rf copyparty/web/deps
|
|
||||||
curl -L https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py >x.py
|
|
||||||
python3 x.py --version
|
|
||||||
rm x.py
|
|
||||||
cp -R /tmp/pe-copyparty.$(id -u)/copyparty/web/deps copyparty/web/
|
|
||||||
```
|
|
||||||
|
|
||||||
or you could build the web-dependencies from source instead (NB: does not include prismjs, need to grab that manually):
|
|
||||||
```sh
|
|
||||||
make -C scripts/deps-docker
|
|
||||||
```
|
|
||||||
|
|
||||||
then build the sfx using any of the following examples:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./scripts/make-sfx.sh # regular edition
|
./scripts/make-sfx.sh # regular edition
|
||||||
|
./scripts/make-sfx.sh fast # build faster (worse js/css compression)
|
||||||
./scripts/make-sfx.sh gz no-cm # gzip-compressed + no fancy markdown editor
|
./scripts/make-sfx.sh gz no-cm # gzip-compressed + no fancy markdown editor
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -285,6 +273,8 @@ python3 setup.py install --skip-build --prefix=/usr --root=$HOME/pe/copyparty
|
|||||||
|
|
||||||
also builds the sfx so skip the sfx section above
|
also builds the sfx so skip the sfx section above
|
||||||
|
|
||||||
|
*WARNING: `rls.sh` has not yet been updated with the docker-images and arch/nix packaging*
|
||||||
|
|
||||||
does everything completely from scratch, straight from your local repo
|
does everything completely from scratch, straight from your local repo
|
||||||
|
|
||||||
in the `scripts` folder:
|
in the `scripts` folder:
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ target-version = ['py27']
|
|||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "black"
|
profile = "black"
|
||||||
include_trailing_comma = true
|
include_trailing_comma = true
|
||||||
force_sort_within_sections = true
|
|
||||||
|
|
||||||
[tool.bandit]
|
[tool.bandit]
|
||||||
skips = ["B104", "B110", "B112"]
|
skips = ["B104", "B110", "B112"]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# TODO easymde embeds codemirror on 3.17 due to new npm probably
|
FROM alpine:3.18
|
||||||
FROM alpine:3.16
|
|
||||||
WORKDIR /z
|
WORKDIR /z
|
||||||
ENV ver_asmcrypto=c72492f4a66e17a0e5dd8ad7874de354f3ccdaa5 \
|
ENV ver_asmcrypto=c72492f4a66e17a0e5dd8ad7874de354f3ccdaa5 \
|
||||||
ver_hashwasm=4.9.0 \
|
ver_hashwasm=4.9.0 \
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
|||||||
org.opencontainers.image.licenses="MIT" \
|
org.opencontainers.image.licenses="MIT" \
|
||||||
org.opencontainers.image.title="copyparty-ac" \
|
org.opencontainers.image.title="copyparty-ac" \
|
||||||
org.opencontainers.image.description="copyparty with Pillow and FFmpeg (image/audio/video thumbnails, audio transcoding, media tags)"
|
org.opencontainers.image.description="copyparty with Pillow and FFmpeg (image/audio/video thumbnails, audio transcoding, media tags)"
|
||||||
|
ENV PYTHONPYCACHEPREFIX=/tmp/pyc
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add !pyc \
|
||||||
wget \
|
wget \
|
||||||
py3-pillow \
|
py3-pillow \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
|
&& rm -rf /tmp/pyc \
|
||||||
&& mkdir /cfg /w \
|
&& mkdir /cfg /w \
|
||||||
&& chmod 777 /cfg /w \
|
&& chmod 777 /cfg /w \
|
||||||
&& echo % /cfg > initcfg
|
&& echo % /cfg > initcfg
|
||||||
|
|||||||
@@ -5,26 +5,27 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
|||||||
org.opencontainers.image.licenses="MIT" \
|
org.opencontainers.image.licenses="MIT" \
|
||||||
org.opencontainers.image.title="copyparty-dj" \
|
org.opencontainers.image.title="copyparty-dj" \
|
||||||
org.opencontainers.image.description="copyparty with all optional dependencies, including musical key / bpm detection"
|
org.opencontainers.image.description="copyparty with all optional dependencies, including musical key / bpm detection"
|
||||||
|
ENV PYTHONPYCACHEPREFIX=/tmp/pyc
|
||||||
|
|
||||||
COPY i/bin/mtag/install-deps.sh ./
|
COPY i/bin/mtag/install-deps.sh ./
|
||||||
COPY i/bin/mtag/audio-bpm.py /mtag/
|
COPY i/bin/mtag/audio-bpm.py /mtag/
|
||||||
COPY i/bin/mtag/audio-key.py /mtag/
|
COPY i/bin/mtag/audio-key.py /mtag/
|
||||||
RUN apk add -U \
|
RUN apk add -U !pyc \
|
||||||
wget \
|
wget \
|
||||||
py3-pillow py3-pip py3-cffi \
|
py3-pillow py3-pip py3-cffi \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
vips-jxl vips-heif vips-poppler vips-magick \
|
vips-jxl vips-heif vips-poppler vips-magick \
|
||||||
py3-numpy fftw libsndfile \
|
py3-numpy fftw libsndfile \
|
||||||
vamp-sdk vamp-sdk-libs \
|
vamp-sdk vamp-sdk-libs \
|
||||||
&& python3 -m pip install pyvips \
|
&& apk add -t .bd \
|
||||||
&& apk --no-cache add -t .bd \
|
|
||||||
bash wget gcc g++ make cmake patchelf \
|
bash wget gcc g++ make cmake patchelf \
|
||||||
python3-dev ffmpeg-dev fftw-dev libsndfile-dev \
|
python3-dev ffmpeg-dev fftw-dev libsndfile-dev \
|
||||||
py3-wheel py3-numpy-dev \
|
py3-wheel py3-numpy-dev \
|
||||||
vamp-sdk-dev \
|
vamp-sdk-dev \
|
||||||
|
&& python3 -m pip install pyvips \
|
||||||
&& bash install-deps.sh \
|
&& bash install-deps.sh \
|
||||||
&& apk del py3-pip .bd \
|
&& apk del py3-pip .bd \
|
||||||
&& rm -rf /var/cache/apk/* \
|
&& rm -rf /var/cache/apk/* /tmp/pyc \
|
||||||
&& chmod 777 /root \
|
&& chmod 777 /root \
|
||||||
&& ln -s /root/vamp /root/.local / \
|
&& ln -s /root/vamp /root/.local / \
|
||||||
&& mkdir /cfg /w \
|
&& mkdir /cfg /w \
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
|||||||
org.opencontainers.image.licenses="MIT" \
|
org.opencontainers.image.licenses="MIT" \
|
||||||
org.opencontainers.image.title="copyparty-im" \
|
org.opencontainers.image.title="copyparty-im" \
|
||||||
org.opencontainers.image.description="copyparty with Pillow and Mutagen (image thumbnails, media tags)"
|
org.opencontainers.image.description="copyparty with Pillow and Mutagen (image thumbnails, media tags)"
|
||||||
|
ENV PYTHONPYCACHEPREFIX=/tmp/pyc
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add !pyc \
|
||||||
wget \
|
wget \
|
||||||
py3-pillow py3-mutagen \
|
py3-pillow py3-mutagen \
|
||||||
|
&& rm -rf /tmp/pyc \
|
||||||
&& mkdir /cfg /w \
|
&& mkdir /cfg /w \
|
||||||
&& chmod 777 /cfg /w \
|
&& chmod 777 /cfg /w \
|
||||||
&& echo % /cfg > initcfg
|
&& echo % /cfg > initcfg
|
||||||
|
|||||||
@@ -5,14 +5,19 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
|||||||
org.opencontainers.image.licenses="MIT" \
|
org.opencontainers.image.licenses="MIT" \
|
||||||
org.opencontainers.image.title="copyparty-iv" \
|
org.opencontainers.image.title="copyparty-iv" \
|
||||||
org.opencontainers.image.description="copyparty with Pillow, FFmpeg, libvips (image/audio/video thumbnails, audio transcoding, media tags)"
|
org.opencontainers.image.description="copyparty with Pillow, FFmpeg, libvips (image/audio/video thumbnails, audio transcoding, media tags)"
|
||||||
|
ENV PYTHONPYCACHEPREFIX=/tmp/pyc
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
RUN apk add -U !pyc \
|
||||||
wget \
|
wget \
|
||||||
py3-pillow py3-pip py3-cffi \
|
py3-pillow py3-pip py3-cffi \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
vips-jxl vips-heif vips-poppler vips-magick \
|
vips-jxl vips-heif vips-poppler vips-magick \
|
||||||
|
&& apk add -t .bd \
|
||||||
|
bash wget gcc g++ make cmake patchelf \
|
||||||
|
python3-dev py3-wheel \
|
||||||
&& python3 -m pip install pyvips \
|
&& python3 -m pip install pyvips \
|
||||||
&& apk del py3-pip \
|
&& apk del py3-pip .bd \
|
||||||
|
&& rm -rf /var/cache/apk/* /tmp/pyc \
|
||||||
&& mkdir /cfg /w \
|
&& mkdir /cfg /w \
|
||||||
&& chmod 777 /cfg /w \
|
&& chmod 777 /cfg /w \
|
||||||
&& echo % /cfg > initcfg
|
&& echo % /cfg > initcfg
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
|||||||
org.opencontainers.image.licenses="MIT" \
|
org.opencontainers.image.licenses="MIT" \
|
||||||
org.opencontainers.image.title="copyparty-min" \
|
org.opencontainers.image.title="copyparty-min" \
|
||||||
org.opencontainers.image.description="just copyparty, no thumbnails / media tags / audio transcoding"
|
org.opencontainers.image.description="just copyparty, no thumbnails / media tags / audio transcoding"
|
||||||
|
ENV PYTHONPYCACHEPREFIX=/tmp/pyc
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add !pyc \
|
||||||
python3 \
|
python3 \
|
||||||
|
&& rm -rf /tmp/pyc \
|
||||||
&& mkdir /cfg /w \
|
&& mkdir /cfg /w \
|
||||||
&& chmod 777 /cfg /w \
|
&& chmod 777 /cfg /w \
|
||||||
&& echo % /cfg > initcfg
|
&& echo % /cfg > initcfg
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
|||||||
org.opencontainers.image.licenses="MIT" \
|
org.opencontainers.image.licenses="MIT" \
|
||||||
org.opencontainers.image.title="copyparty-min-pip" \
|
org.opencontainers.image.title="copyparty-min-pip" \
|
||||||
org.opencontainers.image.description="just copyparty, no thumbnails, no media tags, no audio transcoding"
|
org.opencontainers.image.description="just copyparty, no thumbnails, no media tags, no audio transcoding"
|
||||||
|
ENV PYTHONPYCACHEPREFIX=/tmp/pyc
|
||||||
|
|
||||||
RUN apk --no-cache add python3 py3-pip \
|
RUN apk --no-cache add python3 py3-pip !pyc \
|
||||||
&& python3 -m pip install copyparty \
|
&& python3 -m pip install copyparty \
|
||||||
&& apk del py3-pip \
|
&& apk del py3-pip \
|
||||||
|
&& rm -rf /tmp/pyc \
|
||||||
&& mkdir /cfg /w \
|
&& mkdir /cfg /w \
|
||||||
&& chmod 777 /cfg /w \
|
&& chmod 777 /cfg /w \
|
||||||
&& echo % /cfg > initcfg
|
&& echo % /cfg > initcfg
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ filt=
|
|||||||
[ $(jobs -p | wc -l) -lt $(nproc) ] && break
|
[ $(jobs -p | wc -l) -lt $(nproc) ] && break
|
||||||
while [ -e .blk ]; do sleep 0.2; done
|
while [ -e .blk ]; do sleep 0.2; done
|
||||||
done
|
done
|
||||||
aa="$(printf '%7s' $a)"
|
aa="$(printf '%11s' $a-$i)"
|
||||||
|
|
||||||
# arm takes forever so make it top priority
|
# arm takes forever so make it top priority
|
||||||
[ ${a::3} == arm ] && nice= || nice=nice
|
[ ${a::3} == arm ] && nice= || nice=nice
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
set -e
|
set -e
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
berr() { p=$(head -c 72 </dev/zero | tr '\0' =); printf '\n%s\n\n' $p; cat; printf '\n%s\n\n' $p; }
|
||||||
|
|
||||||
help() { exec cat <<'EOF'
|
help() { exec cat <<'EOF'
|
||||||
|
|
||||||
# optional args:
|
# optional args:
|
||||||
#
|
#
|
||||||
|
# `fast` builds faster, with cheaper js/css compression
|
||||||
|
#
|
||||||
# `clean` uses files from git (everything except web/deps),
|
# `clean` uses files from git (everything except web/deps),
|
||||||
# so local changes won't affect the produced sfx
|
# so local changes won't affect the produced sfx
|
||||||
#
|
#
|
||||||
@@ -42,6 +46,13 @@ help() { exec cat <<'EOF'
|
|||||||
#
|
#
|
||||||
# `no-dd` saves ~2k by removing the mouse cursor
|
# `no-dd` saves ~2k by removing the mouse cursor
|
||||||
#
|
#
|
||||||
|
# _____________________________________________________________________
|
||||||
|
# build behavior:
|
||||||
|
#
|
||||||
|
# `dl-wd` automatically downloads webdeps if necessary
|
||||||
|
#
|
||||||
|
# `ign-wd` allows building an sfx without webdeps
|
||||||
|
#
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# if you are on windows, you can use msys2:
|
# if you are on windows, you can use msys2:
|
||||||
@@ -109,6 +120,8 @@ while [ ! -z "$1" ]; do
|
|||||||
no-hl) no_hl=1 ; ;;
|
no-hl) no_hl=1 ; ;;
|
||||||
no-dd) no_dd=1 ; ;;
|
no-dd) no_dd=1 ; ;;
|
||||||
no-cm) no_cm=1 ; ;;
|
no-cm) no_cm=1 ; ;;
|
||||||
|
dl-wd) dl_wd=1 ; ;;
|
||||||
|
ign-wd) ign_wd=1 ; ;;
|
||||||
fast) zopf= ; ;;
|
fast) zopf= ; ;;
|
||||||
ultra) ultra=1 ; ;;
|
ultra) ultra=1 ; ;;
|
||||||
lang) shift;langs="$1"; ;;
|
lang) shift;langs="$1"; ;;
|
||||||
@@ -223,7 +236,7 @@ necho() {
|
|||||||
|
|
||||||
# enable this to dynamically remove type hints at startup,
|
# enable this to dynamically remove type hints at startup,
|
||||||
# in case a future python version can use them for performance
|
# in case a future python version can use them for performance
|
||||||
true || (
|
true && (
|
||||||
necho collecting strip-hints
|
necho collecting strip-hints
|
||||||
f=../build/strip-hints-0.1.10.tar.gz
|
f=../build/strip-hints-0.1.10.tar.gz
|
||||||
[ -e $f ] ||
|
[ -e $f ] ||
|
||||||
@@ -283,12 +296,56 @@ necho() {
|
|||||||
rm -f copyparty/stolen/*/README.md
|
rm -f copyparty/stolen/*/README.md
|
||||||
|
|
||||||
# remove type hints before build instead
|
# remove type hints before build instead
|
||||||
(cd copyparty; "$pybin" ../../scripts/strip_hints/a.py; rm uh)
|
(cd copyparty; PYTHONPATH="..:$PYTHONPATH" "$pybin" ../../scripts/strip_hints/a.py; rm uh)
|
||||||
|
|
||||||
licfile=$(realpath copyparty/res/COPYING.txt)
|
licfile=$(realpath copyparty/res/COPYING.txt)
|
||||||
(cd ../scripts; ./genlic.sh "$licfile")
|
(cd ../scripts; ./genlic.sh "$licfile")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ ! -e copyparty/web/deps/mini-fa.woff ] && [ $dl_wd ] && {
|
||||||
|
echo "could not find webdeps; downloading..."
|
||||||
|
url=https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py
|
||||||
|
wget -Ox.py "$url" || curl -L "$url" >x.py
|
||||||
|
|
||||||
|
echo "extracting webdeps..."
|
||||||
|
wdsrc="$("$pybin" x.py --version 2>&1 | tee /dev/stderr | awk '/sfxdir:/{sub(/.*: /,"");print;exit}')"
|
||||||
|
[ "$wdsrc" ] || {
|
||||||
|
echo failed to discover tempdir of reference copyparty-sfx.py
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
rm -rf copyparty/web/deps
|
||||||
|
cp -pvR "$wdsrc/copyparty/web/deps" copyparty/web/
|
||||||
|
|
||||||
|
# also copy it out into the source-tree for next time
|
||||||
|
rm -rf ../copyparty/web/deps
|
||||||
|
cp -pR copyparty/web/deps ../copyparty/web
|
||||||
|
|
||||||
|
rm x.py
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -e copyparty/web/deps/mini-fa.woff ] || [ $ign_wd ] || { berr <<'EOF'
|
||||||
|
ERROR:
|
||||||
|
could not find webdeps; the front-end will not be fully functional
|
||||||
|
|
||||||
|
please choose one of the following:
|
||||||
|
|
||||||
|
A) add the argument "dl-wd" to fix it automatically; this will
|
||||||
|
download copyparty-sfx.py and extract the webdeps from there
|
||||||
|
|
||||||
|
B) build the webdeps from source: make -C scripts/deps-docker
|
||||||
|
|
||||||
|
C) add the argument "ign-wd" to continue building the sfx without webdeps
|
||||||
|
|
||||||
|
alternative A is a good choice if you are only intending to
|
||||||
|
modify the copyparty source code (py/html/css/js) and do not
|
||||||
|
plan to make any changes to the mostly-third-party webdeps
|
||||||
|
|
||||||
|
there may be additional hints in the devnotes:
|
||||||
|
https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#building
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
ver=
|
ver=
|
||||||
[ -z "$repack" ] &&
|
[ -z "$repack" ] &&
|
||||||
git describe --tags >/dev/null 2>/dev/null && {
|
git describe --tags >/dev/null 2>/dev/null && {
|
||||||
@@ -421,7 +478,7 @@ while IFS= read -r f; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# up2k goes from 28k to 22k laff
|
# up2k goes from 28k to 22k laff
|
||||||
awk 'BEGIN{gensub(//,"",1)}' </dev/null &&
|
awk 'BEGIN{gensub(//,"",1)}' </dev/null 2>/dev/null &&
|
||||||
echo entabbening &&
|
echo entabbening &&
|
||||||
find | grep -E '\.css$' | while IFS= read -r f; do
|
find | grep -E '\.css$' | while IFS= read -r f; do
|
||||||
awk '{
|
awk '{
|
||||||
@@ -435,7 +492,9 @@ find | grep -E '\.css$' | while IFS= read -r f; do
|
|||||||
1
|
1
|
||||||
' <$f | sed -r 's/;\}$/}/; /\{\}$/d' >t
|
' <$f | sed -r 's/;\}$/}/; /\{\}$/d' >t
|
||||||
tmv "$f"
|
tmv "$f"
|
||||||
done
|
done ||
|
||||||
|
echo "WARNING: your awk does not have gensub, so the sfx will not have optimal compression"
|
||||||
|
|
||||||
unexpand -h 2>/dev/null &&
|
unexpand -h 2>/dev/null &&
|
||||||
find | grep -E '\.(js|html)$' | while IFS= read -r f; do
|
find | grep -E '\.(js|html)$' | while IFS= read -r f; do
|
||||||
unexpand -t 4 --first-only <"$f" >t
|
unexpand -t 4 --first-only <"$f" >t
|
||||||
@@ -529,7 +588,7 @@ sed -r 's/([^ ]*) (.*)/\2.\1/' | grep -vE '/list1?$' > list1
|
|||||||
for n in {1..50}; do
|
for n in {1..50}; do
|
||||||
(grep -vE '\.(gz|br)$' list1; grep -E '\.(gz|br)$' list1 | (shuf||gshuf) ) >list || true
|
(grep -vE '\.(gz|br)$' list1; grep -E '\.(gz|br)$' list1 | (shuf||gshuf) ) >list || true
|
||||||
s=$( (sha1sum||shasum) < list | cut -c-16)
|
s=$( (sha1sum||shasum) < list | cut -c-16)
|
||||||
grep -q $s "$zdir/h" && continue
|
grep -q $s "$zdir/h" 2>/dev/null && continue
|
||||||
echo $s >> "$zdir/h"
|
echo $s >> "$zdir/h"
|
||||||
break
|
break
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
d5510a24cb5e15d6d30677335bbc7624c319b371c0513981843dc51d9b3a1e027661096dfcfc540634222bb2634be6db55bf95185b30133cb884f1e47652cf53 altgraph-0.17.3-py2.py3-none-any.whl
|
d5510a24cb5e15d6d30677335bbc7624c319b371c0513981843dc51d9b3a1e027661096dfcfc540634222bb2634be6db55bf95185b30133cb884f1e47652cf53 altgraph-0.17.3-py2.py3-none-any.whl
|
||||||
eda6c38fc4d813fee897e969ff9ecc5acc613df755ae63df0392217bbd67408b5c1f6c676f2bf5497b772a3eb4e1a360e1245e1c16ee83f0af555f1ab82c3977 Git-2.39.1-32-bit.exe
|
eda6c38fc4d813fee897e969ff9ecc5acc613df755ae63df0392217bbd67408b5c1f6c676f2bf5497b772a3eb4e1a360e1245e1c16ee83f0af555f1ab82c3977 Git-2.39.1-32-bit.exe
|
||||||
17ce52ba50692a9d964f57a23ac163fb74c77fdeb2ca988a6d439ae1fe91955ff43730c073af97a7b3223093ffea3479a996b9b50ee7fba0869247a56f74baa6 pefile-2023.2.7-py3-none-any.whl
|
17ce52ba50692a9d964f57a23ac163fb74c77fdeb2ca988a6d439ae1fe91955ff43730c073af97a7b3223093ffea3479a996b9b50ee7fba0869247a56f74baa6 pefile-2023.2.7-py3-none-any.whl
|
||||||
d68c78bc83f4f48c604912b2d1ca4772b0e6ed676cd2eb439411e0a74d63fe215aac93dd9dab04ed341909a4a6a1efc13ec982516e3cb0fc7c355055e63d9178 pyinstaller-5.10.1-py3-none-win32.whl
|
2410f79f25b55829169fdd45611c04f51932f7701c0601df64ade0eb545c96ba950b7be186eb082482506bc689fcde5fe09c1f6f7cd77c2107028959b7e0d06f pyinstaller-5.12.0-py3-none-win32.whl
|
||||||
fe62705893c86eeb2d5b841da8debe05dedda98364dec190b487e718caad8a8735503bf93739a7a27ea793a835bf976fb919ceec1424b8fc550b936bae4a54e9 pyinstaller-5.10.1-py3-none-win_amd64.whl
|
62f4f3dda0526ea88cfc5af1806c7b53094672f4237d64c088626c226ad2fbc7549f6c9c6bbe5b228b1f87faf1e5c343ec468c485e4c17fe6d79c6b1f570153a pyinstaller-5.12.0-py3-none-win_amd64.whl
|
||||||
61c543983ff67e2bdff94d2d6198023679437363db8c660fa81683aff87c5928cd800720488e18d09be89fe45d6ab99be3ccb912cb2e03e2bca385b4338e1e42 pyinstaller_hooks_contrib-2023.2-py2.py3-none-any.whl
|
2612c263f73a02eab41404ba96e0c7cf8be4475104668b47dfbae50fadf977b3621dd4102682b301264d82b6e130d95ea84a28bf2106a626a1a2845dac16df47 pyinstaller_hooks_contrib-2023.3-py2.py3-none-any.whl
|
||||||
132a5380f33a245f2e744413a0e1090bc42b7356376de5121397cec5976b04b79f7c9ebe28af222c9c7b01461f7d7920810d220e337694727e0d7cd9e91fa667 pywin32_ctypes-0.2.0-py2.py3-none-any.whl
|
132a5380f33a245f2e744413a0e1090bc42b7356376de5121397cec5976b04b79f7c9ebe28af222c9c7b01461f7d7920810d220e337694727e0d7cd9e91fa667 pywin32_ctypes-0.2.0-py2.py3-none-any.whl
|
||||||
3c5adf0a36516d284a2ede363051edc1bcc9df925c5a8a9fa2e03cab579dd8d847fdad42f7fd5ba35992e08234c97d2dbfec40a9d12eec61c8dc03758f2bd88e typing_extensions-4.4.0-py3-none-any.whl
|
3c5adf0a36516d284a2ede363051edc1bcc9df925c5a8a9fa2e03cab579dd8d847fdad42f7fd5ba35992e08234c97d2dbfec40a9d12eec61c8dc03758f2bd88e typing_extensions-4.4.0-py3-none-any.whl
|
||||||
4b6e9ae967a769fe32be8cf0bc0d5a213b138d1e0344e97656d08a3d15578d81c06c45b334c872009db2db8f39db0c77c94ff6c35168d5e13801917667c08678 upx-4.0.2-win32.zip
|
4b6e9ae967a769fe32be8cf0bc0d5a213b138d1e0344e97656d08a3d15578d81c06c45b334c872009db2db8f39db0c77c94ff6c35168d5e13801917667c08678 upx-4.0.2-win32.zip
|
||||||
@@ -24,7 +24,7 @@ c06b3295d1d0b0f0a6f9a6cd0be861b9b643b4a5ea37857f0bd41c45deaf27bb927b71922dab74e6
|
|||||||
ba91ab0518c61eff13e5612d9e6b532940813f6b56e6ed81ea6c7c4d45acee4d98136a383a25067512b8f75538c67c987cf3944bfa0229e3cb677e2fb81e763e zipp-3.10.0-py3-none-any.whl
|
ba91ab0518c61eff13e5612d9e6b532940813f6b56e6ed81ea6c7c4d45acee4d98136a383a25067512b8f75538c67c987cf3944bfa0229e3cb677e2fb81e763e zipp-3.10.0-py3-none-any.whl
|
||||||
# win10
|
# win10
|
||||||
00558cca2e0ac813d404252f6e5aeacb50546822ecb5d0570228b8ddd29d94e059fbeb6b90393dee5abcddaca1370aca784dc9b095cbb74e980b3c024767fb24 Jinja2-3.1.2-py3-none-any.whl
|
00558cca2e0ac813d404252f6e5aeacb50546822ecb5d0570228b8ddd29d94e059fbeb6b90393dee5abcddaca1370aca784dc9b095cbb74e980b3c024767fb24 Jinja2-3.1.2-py3-none-any.whl
|
||||||
b1db6f5a79fc15391547643e5973cf5946c0acfa6febb68bc90fc3f66369681100cc100f32dd04256dcefa510e7864c718515a436a4af3a10fe205c413c7e693 MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl
|
7f8f4daa4f4f2dbf24cdd534b2952ee3fba6334eb42b37465ccda3aa1cccc3d6204aa6bfffb8a83bf42ec59c702b5b5247d4c8ee0d4df906334ae53072ef8c4c MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl
|
||||||
4a20aeb52d4fde6aabcba05ee261595eeb5482c72ee27332690f34dd6e7a49c0b3ba3813202ac15c9d21e29f1cd803f2e79ccc1c45ec314fcd0a937016bcbc56 mutagen-1.46.0-py3-none-any.whl
|
4a20aeb52d4fde6aabcba05ee261595eeb5482c72ee27332690f34dd6e7a49c0b3ba3813202ac15c9d21e29f1cd803f2e79ccc1c45ec314fcd0a937016bcbc56 mutagen-1.46.0-py3-none-any.whl
|
||||||
78414808cb9a5fa74e7b23360b8f46147952530e3cc78a3ad4b80be3e26598080537ac691a1be1f35b7428a22c1f65a6adf45986da2752fbe9d9819d77a58bf8 Pillow-9.5.0-cp311-cp311-win_amd64.whl
|
78414808cb9a5fa74e7b23360b8f46147952530e3cc78a3ad4b80be3e26598080537ac691a1be1f35b7428a22c1f65a6adf45986da2752fbe9d9819d77a58bf8 Pillow-9.5.0-cp311-cp311-win_amd64.whl
|
||||||
4b7711b950858f459d47145b88ccde659279c6af47144d58a1c54ea2ce4b80ec43eb7f69c68f12f8f6bc54c86a44e77441993257f7ad43aab364655de5c51bb1 python-3.11.2-amd64.exe
|
a48ee8992eee60a0d620dced71b9f96596f5dd510e3024015aca55884cdb3f9e2405734bfc13f3f40b79106a77bc442cce02ac4c8f5d16207448052b368fd52a python-3.11.4-amd64.exe
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ a = Analysis(
|
|||||||
|
|
||||||
# this is the only change to the autogenerated specfile:
|
# this is the only change to the autogenerated specfile:
|
||||||
xdll = ["libcrypto-1_1.dll"]
|
xdll = ["libcrypto-1_1.dll"]
|
||||||
a.binaries = TOC([x for x in a.binaries if x[0] not in xdll])
|
a.binaries = [x for x in a.binaries if x[0] not in xdll]
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ copyparty/broker_mp.py,
|
|||||||
copyparty/broker_mpw.py,
|
copyparty/broker_mpw.py,
|
||||||
copyparty/broker_thr.py,
|
copyparty/broker_thr.py,
|
||||||
copyparty/broker_util.py,
|
copyparty/broker_util.py,
|
||||||
|
copyparty/cert.py,
|
||||||
copyparty/cfg.py,
|
copyparty/cfg.py,
|
||||||
copyparty/dxml.py,
|
copyparty/dxml.py,
|
||||||
copyparty/fsutil.py,
|
copyparty/fsutil.py,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class Cfg(Namespace):
|
|||||||
def __init__(self, a=None, v=None, c=None):
|
def __init__(self, a=None, v=None, c=None):
|
||||||
ka = {}
|
ka = {}
|
||||||
|
|
||||||
ex = "daw dav_auth dav_inf dav_mac dav_rt dotsrch e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp force_js getmod hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_thumb no_vthumb no_zip nrand nw rand vc xdev xlink xvol"
|
ex = "daw dav_auth dav_inf dav_mac dav_rt dotsrch e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_thumb no_vthumb no_zip nrand nw rand vc xdev xlink xvol"
|
||||||
ka.update(**{k: False for k in ex.split()})
|
ka.update(**{k: False for k in ex.split()})
|
||||||
|
|
||||||
ex = "dotpart no_rescan no_sendfile no_voldump plain_ip"
|
ex = "dotpart no_rescan no_sendfile no_voldump plain_ip"
|
||||||
@@ -113,7 +113,7 @@ class Cfg(Namespace):
|
|||||||
ex = "df loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp theme themes turbo"
|
ex = "df loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp theme themes turbo"
|
||||||
ka.update(**{k: 0 for k in ex.split()})
|
ka.update(**{k: 0 for k in ex.split()})
|
||||||
|
|
||||||
ex = "doctitle favico html_head lg_sbf log_fk md_sbf mth textfiles R RS SR"
|
ex = "doctitle favico html_head lg_sbf log_fk md_sbf mth textfiles unlist R RS SR"
|
||||||
ka.update(**{k: "" for k in ex.split()})
|
ka.update(**{k: "" for k in ex.split()})
|
||||||
|
|
||||||
ex = "xad xar xau xbd xbr xbu xiu xm"
|
ex = "xad xar xau xbd xbr xbu xiu xm"
|
||||||
|
|||||||
Reference in New Issue
Block a user