mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
Merge branch '9001:hovudstraum' into hovudstraum
This commit is contained in:
commit
d932434d79
23
README.md
23
README.md
|
@ -80,6 +80,7 @@ made in Norway 🇳🇴
|
|||
* [periodic rescan](#periodic-rescan) - filesystem monitoring
|
||||
* [upload rules](#upload-rules) - set upload rules using volflags
|
||||
* [compress uploads](#compress-uploads) - files can be autocompressed on upload
|
||||
* [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
|
||||
* [other flags](#other-flags)
|
||||
* [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
|
||||
* [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
|
||||
|
@ -151,6 +152,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
|
|||
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
||||
* or if you are on android, [install copyparty in termux](#install-on-android)
|
||||
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
|
||||
* or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty`
|
||||
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
|
||||
* or if your OS is dead, give the [bootable flashdrive / cd-rom](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) a spin
|
||||
* or if you don't trust copyparty yet and want to isolate it a little, then...
|
||||
|
@ -1649,6 +1651,26 @@ some examples,
|
|||
allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
|
||||
|
||||
|
||||
## chmod and chown
|
||||
|
||||
per-volume filesystem-permissions and ownership
|
||||
|
||||
by default:
|
||||
* all folders are chmod 755
|
||||
* files are usually chmod 644 (umask-defined)
|
||||
* user/group is whatever copyparty is running as
|
||||
|
||||
this can be configured per-volume:
|
||||
* volflag `chmod_f` sets file permissions; default=`644` (usually)
|
||||
* volflag `chmod_d` sets directory permissions; default=`755`
|
||||
* volflag `uid` sets the owner user-id
|
||||
* volflag `gid` sets the owner group-id
|
||||
|
||||
notes:
|
||||
* `gid` can only be set to one of the groups which the copyparty process is a member of
|
||||
* `uid` can only be set if copyparty is running as root (i appreciate your faith)
|
||||
|
||||
|
||||
## other flags
|
||||
|
||||
* `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
|
||||
|
@ -2226,6 +2248,7 @@ force-enable features with known issues on your OS/env by setting any of the fo
|
|||
| env-var | what it does |
|
||||
| ------------------------ | ------------ |
|
||||
| `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
|
||||
| `PRTY_FORCE_MAGIC` | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |
|
||||
|
||||
|
||||
# packages
|
||||
|
|
|
@ -52,6 +52,7 @@ if PY2:
|
|||
|
||||
sys.dont_write_bytecode = True
|
||||
bytes = str
|
||||
files_decoder = lambda s: unicode(s, 'utf8')
|
||||
else:
|
||||
from urllib.parse import quote_from_bytes as quote
|
||||
from urllib.parse import unquote_to_bytes as unquote
|
||||
|
@ -61,6 +62,7 @@ else:
|
|||
from queue import Queue
|
||||
|
||||
unicode = str
|
||||
files_decoder = unicode
|
||||
|
||||
|
||||
WTF8 = "replace" if PY2 else "surrogateescape"
|
||||
|
@ -1532,7 +1534,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|||
""")
|
||||
|
||||
ap.add_argument("url", type=unicode, help="server url, including destination folder")
|
||||
ap.add_argument("files", type=unicode, nargs="+", help="files and/or folders to process")
|
||||
ap.add_argument("files", type=files_decoder, nargs="+", help="files and/or folders to process")
|
||||
ap.add_argument("-v", action="store_true", help="verbose")
|
||||
ap.add_argument("-a", metavar="PASSWD", help="password or $filepath")
|
||||
ap.add_argument("-s", action="store_true", help="file-search (disables upload)")
|
||||
|
|
|
@ -22,7 +22,7 @@ let
|
|||
mkValueString =
|
||||
value:
|
||||
if isList value then
|
||||
(concatStringsSep ", " (map mkValueString value))
|
||||
(concatStringsSep "," (map mkValueString value))
|
||||
else if isAttrs value then
|
||||
"\n" + (mkAttrsString value)
|
||||
else
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.18.6"
|
||||
pkgver="1.18.9"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
@ -23,7 +23,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")
|
||||
backup=("etc/${pkgname}.d/init" )
|
||||
sha256sums=("80762d91ac88815e73d0ca2806c6391dcf8ccd521bc402cc312349f3bc8e8b28")
|
||||
sha256sums=("d5d33b50d6717e52427956beb1687061a6f28b467997506505151e3ae18c58e5")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
44
contrib/package/makedeb-mpr/PKGBUILD
Normal file
44
contrib/package/makedeb-mpr/PKGBUILD
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Contributor: Beethoven <beethovenisadog@protonmail.com>
|
||||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.18.9
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
url="https://github.com/9001/${pkgname}"
|
||||
license=('MIT')
|
||||
depends=("bash" "python3" "lsof" "python3-jinja2")
|
||||
makedepends=("python3-wheel" "python3-setuptools" "python3-build" "python3-installer" "make" "pigz")
|
||||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||
"golang-cfssl: generate TLS certificates on startup"
|
||||
"python3-mutagen: music tags (alternative)"
|
||||
"python3-pil: thumbnails for images"
|
||||
"python3-openssl: ftps functionality"
|
||||
"python3-zmq: send zeromq messages from event-hooks"
|
||||
"python3-argon2: hashed passwords in config"
|
||||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("/etc/${pkgname}.d/init" )
|
||||
sha256sums=("d5d33b50d6717e52427956beb1687061a6f28b467997506505151e3ae18c58e5")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
make
|
||||
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
python -m build --wheel --no-isolation
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
||||
|
||||
install -dm755 "${pkgdir}/etc/${pkgname}.d"
|
||||
install -Dm755 "bin/prisonparty.sh" "${pkgdir}/usr/bin/prisonparty"
|
||||
install -Dm644 "contrib/package/makedeb-mpr/${pkgname}.conf" "${pkgdir}/etc/${pkgname}.d/init"
|
||||
install -Dm644 "contrib/package/makedeb-mpr/${pkgname}.service" "${pkgdir}/usr/lib/systemd/system/${pkgname}.service"
|
||||
install -Dm644 "contrib/package/makedeb-mpr/prisonparty.service" "${pkgdir}/usr/lib/systemd/system/prisonparty.service"
|
||||
install -Dm644 "contrib/package/makedeb-mpr/index.md" "${pkgdir}/var/lib/${pkgname}-jail/README.md"
|
||||
install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
}
|
7
contrib/package/makedeb-mpr/copyparty.conf
Normal file
7
contrib/package/makedeb-mpr/copyparty.conf
Normal file
|
@ -0,0 +1,7 @@
|
|||
## import all *.conf files from the current folder (/etc/copyparty.d)
|
||||
% ./
|
||||
|
||||
# add additional .conf files to this folder;
|
||||
# see example config files for reference:
|
||||
# https://github.com/9001/copyparty/blob/hovudstraum/docs/example.conf
|
||||
# https://github.com/9001/copyparty/tree/hovudstraum/docs/copyparty.d
|
32
contrib/package/makedeb-mpr/copyparty.service
Normal file
32
contrib/package/makedeb-mpr/copyparty.service
Normal file
|
@ -0,0 +1,32 @@
|
|||
# this will start `/usr/bin/copyparty-sfx.py`
|
||||
# and read config from `/etc/copyparty.d/*.conf`
|
||||
#
|
||||
# you probably want to:
|
||||
# change "User=cpp" and "/home/cpp/" to another user
|
||||
#
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
# Environment=PYTHONUNBUFFERED=x
|
||||
|
||||
[Unit]
|
||||
Description=copyparty file server
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
SyslogIdentifier=copyparty
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
WorkingDirectory=/var/lib/copyparty-jail
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
|
||||
# user to run as + where the TLS certificate is (if any)
|
||||
User=cpp
|
||||
Environment=XDG_CONFIG_HOME=/home/cpp/.config
|
||||
|
||||
# stop systemd-tmpfiles-clean.timer from deleting copyparty while it's running
|
||||
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||
|
||||
# run copyparty
|
||||
ExecStart=/usr/bin/python3 /usr/bin/copyparty -c /etc/copyparty.d/init
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
3
contrib/package/makedeb-mpr/index.md
Normal file
3
contrib/package/makedeb-mpr/index.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
this is `/var/lib/copyparty-jail`, the fallback webroot when copyparty has not yet been configured
|
||||
|
||||
please add some `*.conf` files to `/etc/copyparty.d/`
|
33
contrib/package/makedeb-mpr/prisonparty.service
Normal file
33
contrib/package/makedeb-mpr/prisonparty.service
Normal file
|
@ -0,0 +1,33 @@
|
|||
# this will start `/usr/bin/copyparty-sfx.py`
|
||||
# in a chroot, preventing accidental access elsewhere,
|
||||
# and read copyparty config from `/etc/copyparty.d/*.conf`
|
||||
#
|
||||
# expose additional filesystem locations to copyparty
|
||||
# by listing them between the last `cpp` and `--`
|
||||
#
|
||||
# `cpp cpp` = user/group to run copyparty as; can be IDs (1000 1000)
|
||||
#
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
# Environment=PYTHONUNBUFFERED=x
|
||||
|
||||
[Unit]
|
||||
Description=copyparty file server
|
||||
|
||||
[Service]
|
||||
SyslogIdentifier=prisonparty
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
WorkingDirectory=/var/lib/copyparty-jail
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
|
||||
# stop systemd-tmpfiles-clean.timer from deleting copyparty while it's running
|
||||
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||
|
||||
# run copyparty
|
||||
ExecStart=/bin/bash /usr/bin/prisonparty /var/lib/copyparty-jail cpp cpp \
|
||||
/etc/copyparty.d \
|
||||
-- \
|
||||
/usr/bin/python3 /usr/bin/copyparty -c /etc/copyparty.d/init
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.18.6/copyparty-sfx.py",
|
||||
"version": "1.18.6",
|
||||
"hash": "sha256-No89mzKHHZZH19ws9dqfvQO0pnZw7jKDMGhNa4LOFlY="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.18.9/copyparty-sfx.py",
|
||||
"version": "1.18.9",
|
||||
"hash": "sha256-R1OVx4f8GERAG80ZcHAIP6HK2TlBbKJZpvnJmJbGPRY="
|
||||
}
|
|
@ -53,13 +53,13 @@ from .util import (
|
|||
PYFTPD_VER,
|
||||
RAM_AVAIL,
|
||||
RAM_TOTAL,
|
||||
RE_ANSI,
|
||||
SQLITE_VER,
|
||||
UNPLICATIONS,
|
||||
URL_BUG,
|
||||
URL_PRJ,
|
||||
Daemon,
|
||||
align_tab,
|
||||
ansi_re,
|
||||
b64enc,
|
||||
dedent,
|
||||
has_resource,
|
||||
|
@ -167,7 +167,7 @@ def lprint(*a: Any, **ka: Any) -> None:
|
|||
txt: str = " ".join(unicode(x) for x in a) + eol
|
||||
printed.append(txt)
|
||||
if not VT100:
|
||||
txt = ansi_re.sub("", txt)
|
||||
txt = RE_ANSI.sub("", txt)
|
||||
|
||||
print(txt, end="", **ka)
|
||||
|
||||
|
@ -1053,6 +1053,8 @@ def add_upload(ap):
|
|||
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
|
||||
ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
|
||||
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
||||
ap2.add_argument("--uid", metavar="N", type=int, default=-1, help="unix user-id to chown new files/folders to; default = -1 = do-not-change (volflag=uid)")
|
||||
ap2.add_argument("--gid", metavar="N", type=int, default=-1, help="unix group-id to chown new files/folders to; default = -1 = do-not-change (volflag=gid)")
|
||||
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
|
||||
ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
|
||||
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
||||
|
@ -1107,7 +1109,7 @@ def add_tls(ap, cert_path):
|
|||
ap2 = ap.add_argument_group('SSL/TLS options')
|
||||
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext")
|
||||
ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls")
|
||||
ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to TLS certificate")
|
||||
ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to file containing a concatenation of TLS key and certificate chain")
|
||||
ap2.add_argument("--ssl-ver", metavar="LIST", type=u, default="", help="set allowed ssl/tls versions; [\033[32mhelp\033[0m] shows available versions; default is what your python version considers safe")
|
||||
ap2.add_argument("--ciphers", metavar="LIST", type=u, default="", help="set allowed ssl/tls ciphers; [\033[32mhelp\033[0m] shows available ciphers")
|
||||
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
||||
|
@ -1546,6 +1548,7 @@ def add_ui(ap, retry):
|
|||
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("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
||||
ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC")
|
||||
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor chi\033[0m")
|
||||
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
|
||||
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
||||
|
@ -1555,7 +1558,7 @@ def add_ui(ap, retry):
|
|||
ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
|
||||
ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
|
||||
ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
|
||||
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m 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("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: 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("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
||||
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])")
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 18, 6)
|
||||
VERSION = (1, 18, 9)
|
||||
CODENAME = "logtail"
|
||||
BUILD_DT = (2025, 7, 28)
|
||||
BUILD_DT = (2025, 8, 1)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
|
|
@ -33,6 +33,7 @@ from .util import (
|
|||
afsenc,
|
||||
get_df,
|
||||
humansize,
|
||||
json_hesc,
|
||||
min_ex,
|
||||
odfusion,
|
||||
read_utf8,
|
||||
|
@ -70,6 +71,25 @@ if PY2:
|
|||
|
||||
|
||||
LEELOO_DALLAS = "leeloo_dallas"
|
||||
##
|
||||
## you might be curious what Leeloo Dallas is doing here, so let me explain:
|
||||
##
|
||||
## certain daemonic tasks, namely:
|
||||
## * deletion of expired files, running on a timer
|
||||
## * deletion of sidecar files, initiated by plugins
|
||||
## need to skip the usual permission-checks to do their thing,
|
||||
## so we let Leeloo handle these
|
||||
##
|
||||
## and also, the smb-server has really shitty support for user-accounts
|
||||
## so one popular way to avoid issues is by running copyparty without users;
|
||||
## this makes all smb-clients identify as LD to gain unrestricted access
|
||||
##
|
||||
## Leeloo, being a fictional character from The Fifth Element,
|
||||
## obviously does not exist and will never be able to access any copyparty
|
||||
## instances from the outside (the username is rejected at every entrypoint)
|
||||
##
|
||||
## thanks for coming to my ted talk
|
||||
|
||||
|
||||
SEE_LOG = "see log for details"
|
||||
SEESLOG = " (see serverlog for details)"
|
||||
|
@ -121,6 +141,8 @@ class Lim(object):
|
|||
self.reg: Optional[dict[str, dict[str, Any]]] = None # up2k registry
|
||||
|
||||
self.chmod_d = 0o755
|
||||
self.uid = self.gid = -1
|
||||
self.chown = False
|
||||
|
||||
self.nups: dict[str, list[float]] = {} # num tracker
|
||||
self.bups: dict[str, list[tuple[float, int]]] = {} # byte tracker list
|
||||
|
@ -283,6 +305,8 @@ class Lim(object):
|
|||
# no branches yet; make one
|
||||
sub = os.path.join(path, "0")
|
||||
bos.mkdir(sub, self.chmod_d)
|
||||
if self.chown:
|
||||
os.chown(sub, self.uid, self.gid)
|
||||
else:
|
||||
# try newest branch only
|
||||
sub = os.path.join(path, str(dirs[-1]))
|
||||
|
@ -298,6 +322,8 @@ class Lim(object):
|
|||
# make a branch
|
||||
sub = os.path.join(path, str(dirs[-1] + 1))
|
||||
bos.mkdir(sub, self.chmod_d)
|
||||
if self.chown:
|
||||
os.chown(sub, self.uid, self.gid)
|
||||
ret = self.dive(sub, lvs - 1)
|
||||
if ret is None:
|
||||
raise Pebkac(500, "rotation bug")
|
||||
|
@ -2162,7 +2188,7 @@ class AuthSrv(object):
|
|||
if vf not in vol.flags:
|
||||
vol.flags[vf] = getattr(self.args, ga)
|
||||
|
||||
zs = "forget_ip nrand tail_who u2abort u2ow ups_who zip_who"
|
||||
zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
|
||||
for k in zs.split():
|
||||
if k in vol.flags:
|
||||
vol.flags[k] = int(vol.flags[k])
|
||||
|
@ -2199,8 +2225,17 @@ class AuthSrv(object):
|
|||
if (is_d and zi != 0o755) or not is_d:
|
||||
free_umask = True
|
||||
|
||||
vol.flags.pop("chown", None)
|
||||
if vol.flags["uid"] != -1 or vol.flags["gid"] != -1:
|
||||
vol.flags["chown"] = True
|
||||
vol.flags.pop("fperms", None)
|
||||
if "chown" in vol.flags or vol.flags.get("chmod_f"):
|
||||
vol.flags["fperms"] = True
|
||||
if vol.lim:
|
||||
vol.lim.chmod_d = vol.flags["chmod_d"]
|
||||
vol.lim.chown = "chown" in vol.flags
|
||||
vol.lim.uid = vol.flags["uid"]
|
||||
vol.lim.gid = vol.flags["gid"]
|
||||
|
||||
if vol.flags.get("og"):
|
||||
self.args.uqe = True
|
||||
|
@ -2740,6 +2775,7 @@ class AuthSrv(object):
|
|||
"dth3x": vf["th3x"],
|
||||
"dvol": self.args.au_vol,
|
||||
"idxh": int(self.args.ih),
|
||||
"dutc": not self.args.localtime,
|
||||
"themes": self.args.themes,
|
||||
"turbolvl": self.args.turbo,
|
||||
"nosubtle": self.args.nosubtle,
|
||||
|
@ -2751,7 +2787,7 @@ class AuthSrv(object):
|
|||
"lifetime": vn.js_ls["lifetime"],
|
||||
"u2sort": self.args.u2sort,
|
||||
}
|
||||
vn.js_htm = json.dumps(js_htm)
|
||||
vn.js_htm = json_hesc(json.dumps(js_htm))
|
||||
|
||||
vols = list(vfs.all_nodes.values())
|
||||
if enshare:
|
||||
|
@ -3422,7 +3458,7 @@ def expand_config_file(
|
|||
ipath += " -> " + fp
|
||||
ret.append("#\033[36m opening cfg file{}\033[0m".format(ipath))
|
||||
|
||||
cfg_lines = read_utf8(log, fp, True).split("\n")
|
||||
cfg_lines = read_utf8(log, fp, True).replace("\t", " ").split("\n")
|
||||
if True: # diff-golf
|
||||
for oln in [x.rstrip() for x in cfg_lines]:
|
||||
ln = oln.split(" #")[0].strip()
|
||||
|
|
|
@ -9,8 +9,11 @@ from . import path as path
|
|||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Any, Optional
|
||||
|
||||
_ = (path,)
|
||||
__all__ = ["path"]
|
||||
MKD_755 = {"chmod_d": 0o755}
|
||||
MKD_700 = {"chmod_d": 0o700}
|
||||
|
||||
_ = (path, MKD_755, MKD_700)
|
||||
__all__ = ["path", "MKD_755", "MKD_700"]
|
||||
|
||||
# grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
|
||||
# printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
|
||||
|
@ -20,11 +23,15 @@ def chmod(p: str, mode: int) -> None:
|
|||
return os.chmod(fsenc(p), mode)
|
||||
|
||||
|
||||
def chown(p: str, uid: int, gid: int) -> None:
|
||||
return os.chown(fsenc(p), uid, gid)
|
||||
|
||||
|
||||
def listdir(p: str = ".") -> list[str]:
|
||||
return [fsdec(x) for x in os.listdir(fsenc(p))]
|
||||
|
||||
|
||||
def makedirs(name: str, mode: int = 0o755, exist_ok: bool = True) -> bool:
|
||||
def makedirs(name: str, vf: dict[str, Any] = MKD_755, exist_ok: bool = True) -> bool:
|
||||
# os.makedirs does 777 for all but leaf; this does mode on all
|
||||
todo = []
|
||||
bname = fsenc(name)
|
||||
|
@ -37,9 +44,13 @@ def makedirs(name: str, mode: int = 0o755, exist_ok: bool = True) -> bool:
|
|||
if not exist_ok:
|
||||
os.mkdir(bname) # to throw
|
||||
return False
|
||||
mode = vf["chmod_d"]
|
||||
chown = "chown" in vf
|
||||
for zb in todo[::-1]:
|
||||
try:
|
||||
os.mkdir(zb, mode)
|
||||
if chown:
|
||||
os.chown(zb, vf["uid"], vf["gid"])
|
||||
except:
|
||||
if os.path.isdir(zb):
|
||||
continue
|
||||
|
|
|
@ -114,6 +114,8 @@ def vf_vmap() -> dict[str, str]:
|
|||
"unlist",
|
||||
"u2abort",
|
||||
"u2ts",
|
||||
"uid",
|
||||
"gid",
|
||||
"ups_who",
|
||||
"zip_who",
|
||||
"zipmaxn",
|
||||
|
@ -175,6 +177,8 @@ flagcats = {
|
|||
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
||||
"chmod_d=755": "unix-permission for new dirs/folders",
|
||||
"chmod_f=644": "unix-permission for new files",
|
||||
"uid=573": "change owner of new files/folders to unix-user 573",
|
||||
"gid=999": "change owner of new files/folders to unix-group 999",
|
||||
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
||||
"nosparse": "deny use of sparse files, mainly for slow storage",
|
||||
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
||||
|
|
|
@ -31,6 +31,7 @@ from .util import (
|
|||
relchk,
|
||||
runhook,
|
||||
sanitize_fn,
|
||||
set_fperms,
|
||||
vjoin,
|
||||
wunlink,
|
||||
)
|
||||
|
@ -262,8 +263,8 @@ class FtpFs(AbstractedFS):
|
|||
wunlink(self.log, ap, VF_CAREFUL)
|
||||
|
||||
ret = open(fsenc(ap), mode, self.args.iobuf)
|
||||
if w and "chmod_f" in vfs.flags:
|
||||
os.fchmod(ret.fileno(), vfs.flags["chmod_f"])
|
||||
if w and "fperms" in vfs.flags:
|
||||
set_fperms(ret, vfs.flags)
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -297,8 +298,7 @@ class FtpFs(AbstractedFS):
|
|||
|
||||
def mkdir(self, path: str) -> None:
|
||||
ap, vfs, _ = self.rv2a(path, w=True)
|
||||
chmod = vfs.flags["chmod_d"]
|
||||
bos.makedirs(ap, chmod) # filezilla expects this
|
||||
bos.makedirs(ap, vf=vfs.flags) # filezilla expects this
|
||||
|
||||
def listdir(self, path: str) -> list[str]:
|
||||
vpath = join(self.cwd, path)
|
||||
|
|
|
@ -33,7 +33,7 @@ except:
|
|||
|
||||
from .__init__ import ANYWIN, PY2, RES, TYPE_CHECKING, EnvParams, unicode
|
||||
from .__version__ import S_VERSION
|
||||
from .authsrv import VFS # typechk
|
||||
from .authsrv import LEELOO_DALLAS, VFS # typechk
|
||||
from .bos import bos
|
||||
from .star import StreamTar
|
||||
from .stolen.qrcodegen import QrCode, qr2svg
|
||||
|
@ -79,8 +79,10 @@ from .util import (
|
|||
hidedir,
|
||||
html_bescape,
|
||||
html_escape,
|
||||
html_sh_esc,
|
||||
humansize,
|
||||
ipnorm,
|
||||
json_hesc,
|
||||
justcopy,
|
||||
load_resource,
|
||||
loadpy,
|
||||
|
@ -103,7 +105,9 @@ from .util import (
|
|||
sanitize_vpath,
|
||||
sendfile_kern,
|
||||
sendfile_py,
|
||||
set_fperms,
|
||||
stat_resource,
|
||||
str_anchor,
|
||||
ub64dec,
|
||||
ub64enc,
|
||||
ujoin,
|
||||
|
@ -622,6 +626,9 @@ class HttpCli(object):
|
|||
) or self.args.idp_h_key in self.headers
|
||||
|
||||
if trusted_key and trusted_xff:
|
||||
if idp_usr.lower() == LEELOO_DALLAS:
|
||||
self.loud_reply("send her back", status=403)
|
||||
return False
|
||||
self.asrv.idp_checkin(self.conn.hsrv.broker, idp_usr, idp_grp)
|
||||
else:
|
||||
if not trusted_key:
|
||||
|
@ -905,7 +912,7 @@ class HttpCli(object):
|
|||
if status == 304:
|
||||
self.out_headers.pop("Content-Length", None)
|
||||
self.out_headers.pop("Content-Type", None)
|
||||
self.out_headerlist.clear()
|
||||
self.out_headerlist[:] = []
|
||||
if self.k304():
|
||||
self.keepalive = False
|
||||
else:
|
||||
|
@ -1110,15 +1117,18 @@ class HttpCli(object):
|
|||
else:
|
||||
return True
|
||||
|
||||
host = self.host.lower()
|
||||
if host.startswith("["):
|
||||
if "]:" in host:
|
||||
host = host.split("]:")[0] + "]"
|
||||
else:
|
||||
host = host.split(":")[0]
|
||||
|
||||
oh = self.out_headers
|
||||
origin = origin.lower()
|
||||
good_origins = self.args.acao + [
|
||||
"%s://%s"
|
||||
% (
|
||||
"https" if self.is_https else "http",
|
||||
self.host.lower().split(":")[0],
|
||||
)
|
||||
]
|
||||
proto = "https" if self.is_https else "http"
|
||||
good_origins = self.args.acao + ["%s://%s" % (proto, host)]
|
||||
|
||||
if "pw" in ih or re.sub(r"(:[0-9]{1,5})?/?$", "", origin) in good_origins:
|
||||
good_origin = True
|
||||
bad_hdrs = ("",)
|
||||
|
@ -2083,7 +2093,7 @@ class HttpCli(object):
|
|||
fdir, fn = os.path.split(fdir)
|
||||
rem, _ = vsplit(rem)
|
||||
|
||||
bos.makedirs(fdir, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
|
||||
open_ka: dict[str, Any] = {"fun": open}
|
||||
open_a = ["wb", self.args.iobuf]
|
||||
|
@ -2141,9 +2151,7 @@ class HttpCli(object):
|
|||
if nameless:
|
||||
fn = vfs.flags["put_name2"].format(now=time.time(), cip=self.dip())
|
||||
|
||||
params = {"suffix": suffix, "fdir": fdir}
|
||||
if "chmod_f" in vfs.flags:
|
||||
params["chmod"] = vfs.flags["chmod_f"]
|
||||
params = {"suffix": suffix, "fdir": fdir, "vf": vfs.flags}
|
||||
if self.args.nw:
|
||||
params = {}
|
||||
fn = os.devnull
|
||||
|
@ -2192,7 +2200,7 @@ class HttpCli(object):
|
|||
if self.args.nw:
|
||||
fn = os.devnull
|
||||
else:
|
||||
bos.makedirs(fdir, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
path = os.path.join(fdir, fn)
|
||||
if not nameless:
|
||||
self.vpath = vjoin(self.vpath, fn)
|
||||
|
@ -2324,7 +2332,7 @@ class HttpCli(object):
|
|||
if self.args.hook_v:
|
||||
log_reloc(self.log, hr["reloc"], x, path, vp, fn, vfs, rem)
|
||||
fdir, self.vpath, fn, (vfs, rem) = x
|
||||
bos.makedirs(fdir, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
path2 = os.path.join(fdir, fn)
|
||||
atomic_move(self.log, path, path2, vfs.flags)
|
||||
path = path2
|
||||
|
@ -2610,7 +2618,7 @@ class HttpCli(object):
|
|||
dst = vfs.canonical(rem)
|
||||
try:
|
||||
if not bos.path.isdir(dst):
|
||||
bos.makedirs(dst, vfs.flags["chmod_d"])
|
||||
bos.makedirs(dst, vf=vfs.flags)
|
||||
except OSError as ex:
|
||||
self.log("makedirs failed %r" % (dst,))
|
||||
if not bos.path.isdir(dst):
|
||||
|
@ -2933,7 +2941,7 @@ class HttpCli(object):
|
|||
msg = "new password OK"
|
||||
|
||||
redir = (self.args.SRS + "?h") if ok else ""
|
||||
h2 = '<a href="' + self.args.SRS + '?h">ack</a>'
|
||||
h2 = '<a href="' + self.args.SRS + '?h">continue</a>'
|
||||
html = self.j2s("msg", h1=msg, h2=h2, redir=redir)
|
||||
self.reply(html.encode("utf-8"))
|
||||
return True
|
||||
|
@ -2962,7 +2970,8 @@ class HttpCli(object):
|
|||
dst += "_=1#" + html_escape(uhash, True, True)
|
||||
|
||||
_, msg = self.get_pwd_cookie(pwd)
|
||||
html = self.j2s("msg", h1=msg, h2='<a href="' + dst + '">ack</a>', redir=dst)
|
||||
h2 = '<a href="' + dst + '">continue</a>'
|
||||
html = self.j2s("msg", h1=msg, h2=h2, redir=dst)
|
||||
self.reply(html.encode("utf-8"))
|
||||
return True
|
||||
|
||||
|
@ -2976,7 +2985,7 @@ class HttpCli(object):
|
|||
self.get_pwd_cookie("x")
|
||||
|
||||
dst = self.args.SRS + "?h"
|
||||
h2 = '<a href="' + dst + '">ack</a>'
|
||||
h2 = '<a href="' + dst + '">continue</a>'
|
||||
html = self.j2s("msg", h1="ok bye", h2=h2, redir=dst)
|
||||
self.reply(html.encode("utf-8"))
|
||||
return True
|
||||
|
@ -3057,7 +3066,7 @@ class HttpCli(object):
|
|||
raise Pebkac(405, 'folder "/%s" already exists' % (vpath,))
|
||||
|
||||
try:
|
||||
bos.makedirs(fn, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fn, vf=vfs.flags)
|
||||
except OSError as ex:
|
||||
if ex.errno == errno.EACCES:
|
||||
raise Pebkac(500, "the server OS denied write-access")
|
||||
|
@ -3099,8 +3108,22 @@ class HttpCli(object):
|
|||
|
||||
with open(fsenc(fn), "wb") as f:
|
||||
f.write(b"`GRUNNUR`\n")
|
||||
if "chmod_f" in vfs.flags:
|
||||
os.fchmod(f.fileno(), vfs.flags["chmod_f"])
|
||||
if "fperms" in vfs.flags:
|
||||
set_fperms(f, vfs.flags)
|
||||
|
||||
dbv, vrem = vfs.get_dbv(rem)
|
||||
self.conn.hsrv.broker.say(
|
||||
"up2k.hash_file",
|
||||
dbv.realpath,
|
||||
dbv.vpath,
|
||||
dbv.flags,
|
||||
vrem,
|
||||
sanitized,
|
||||
self.ip,
|
||||
bos.stat(fn).st_mtime,
|
||||
self.uname,
|
||||
True,
|
||||
)
|
||||
|
||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||
self.redirect(vpath, "?edit")
|
||||
|
@ -3174,7 +3197,7 @@ class HttpCli(object):
|
|||
)
|
||||
upload_vpath = "{}/{}".format(vfs.vpath, rem).strip("/")
|
||||
if not nullwrite:
|
||||
bos.makedirs(fdir_base, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fdir_base, vf=vfs.flags)
|
||||
|
||||
rnd, lifetime, xbu, xau = self.upload_flags(vfs)
|
||||
zs = self.uparam.get("want") or self.headers.get("accept") or ""
|
||||
|
@ -3207,7 +3230,7 @@ class HttpCli(object):
|
|||
if rnd:
|
||||
fname = rand_name(fdir, fname, rnd)
|
||||
|
||||
open_args = {"fdir": fdir, "suffix": suffix}
|
||||
open_args = {"fdir": fdir, "suffix": suffix, "vf": vfs.flags}
|
||||
|
||||
if "replace" in self.uparam:
|
||||
if not self.can_delete:
|
||||
|
@ -3269,11 +3292,8 @@ class HttpCli(object):
|
|||
else:
|
||||
open_args["fdir"] = fdir
|
||||
|
||||
if "chmod_f" in vfs.flags:
|
||||
open_args["chmod"] = vfs.flags["chmod_f"]
|
||||
|
||||
if p_file and not nullwrite:
|
||||
bos.makedirs(fdir, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
|
||||
# reserve destination filename
|
||||
f, fname = ren_open(fname, "wb", fdir=fdir, suffix=suffix)
|
||||
|
@ -3377,7 +3397,7 @@ class HttpCli(object):
|
|||
if nullwrite:
|
||||
fdir = ap2 = ""
|
||||
else:
|
||||
bos.makedirs(fdir, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
atomic_move(self.log, abspath, ap2, vfs.flags)
|
||||
abspath = ap2
|
||||
sz = bos.path.getsize(abspath)
|
||||
|
@ -3498,8 +3518,8 @@ class HttpCli(object):
|
|||
ft = "{}:{}".format(self.ip, self.addr[1])
|
||||
ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg)
|
||||
f.write(ft.encode("utf-8"))
|
||||
if "chmod_f" in vfs.flags:
|
||||
os.fchmod(f.fileno(), vfs.flags["chmod_f"])
|
||||
if "fperms" in vfs.flags:
|
||||
set_fperms(f, vfs.flags)
|
||||
except Exception as ex:
|
||||
suf = "\nfailed to write the upload report: {}".format(ex)
|
||||
|
||||
|
@ -3550,7 +3570,7 @@ class HttpCli(object):
|
|||
lim = vfs.get_dbv(rem)[0].lim
|
||||
if lim:
|
||||
fp, rp = lim.all(self.ip, rp, clen, vfs.realpath, fp, self.conn.hsrv.broker)
|
||||
bos.makedirs(fp, vfs.flags["chmod_d"])
|
||||
bos.makedirs(fp, vf=vfs.flags)
|
||||
|
||||
fp = os.path.join(fp, fn)
|
||||
rem = "{}/{}".format(rp, fn).strip("/")
|
||||
|
@ -3618,15 +3638,17 @@ class HttpCli(object):
|
|||
zs = ub64enc(zb).decode("ascii")[:24].lower()
|
||||
dp = "%s/md/%s/%s/%s" % (dbv.histpath, zs[:2], zs[2:4], zs)
|
||||
self.log("moving old version to %s/%s" % (dp, mfile2))
|
||||
if bos.makedirs(dp, vfs.flags["chmod_d"]):
|
||||
if bos.makedirs(dp, vf=vfs.flags):
|
||||
with open(os.path.join(dp, "dir.txt"), "wb") as f:
|
||||
f.write(afsenc(vrd))
|
||||
if "chmod_f" in vfs.flags:
|
||||
os.fchmod(f.fileno(), vfs.flags["chmod_f"])
|
||||
if "fperms" in vfs.flags:
|
||||
set_fperms(f, vfs.flags)
|
||||
elif hist_cfg == "s":
|
||||
dp = os.path.join(mdir, ".hist")
|
||||
try:
|
||||
bos.mkdir(dp, vfs.flags["chmod_d"])
|
||||
if "chown" in vfs.flags:
|
||||
bos.chown(dp, vfs.flags["uid"], vfs.flags["gid"])
|
||||
hidedir(dp)
|
||||
except:
|
||||
pass
|
||||
|
@ -3665,8 +3687,8 @@ class HttpCli(object):
|
|||
wunlink(self.log, fp, vfs.flags)
|
||||
|
||||
with open(fsenc(fp), "wb", self.args.iobuf) as f:
|
||||
if "chmod_f" in vfs.flags:
|
||||
os.fchmod(f.fileno(), vfs.flags["chmod_f"])
|
||||
if "fperms" in vfs.flags:
|
||||
set_fperms(f, vfs.flags)
|
||||
sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp)
|
||||
|
||||
if lim:
|
||||
|
@ -4907,11 +4929,8 @@ class HttpCli(object):
|
|||
else:
|
||||
rip = host
|
||||
|
||||
# safer than html_escape/quotep since this avoids both XSS and shell-stuff
|
||||
pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2")
|
||||
vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
|
||||
pw = pw.replace(" ", "%20")
|
||||
vp = vp.replace(" ", "%20")
|
||||
vp = (self.uparam["hc"] or "").lstrip("/")
|
||||
pw = self.pw or "hunter2"
|
||||
if pw in self.asrv.sesa:
|
||||
pw = "hunter2"
|
||||
|
||||
|
@ -4920,14 +4939,14 @@ class HttpCli(object):
|
|||
args=self.args,
|
||||
accs=bool(self.asrv.acct),
|
||||
s="s" if self.is_https else "",
|
||||
rip=rip,
|
||||
ep=ep,
|
||||
vp=vp,
|
||||
rvp=vjoin(self.args.R, vp),
|
||||
host=host,
|
||||
hport=hport,
|
||||
rip=html_sh_esc(rip),
|
||||
ep=html_sh_esc(ep),
|
||||
vp=html_sh_esc(vp),
|
||||
rvp=html_sh_esc(vjoin(self.args.R, vp)),
|
||||
host=html_sh_esc(host),
|
||||
hport=html_sh_esc(hport),
|
||||
aname=aname,
|
||||
pw=pw,
|
||||
pw=html_sh_esc(pw),
|
||||
)
|
||||
self.reply(html.encode("utf-8"))
|
||||
return True
|
||||
|
@ -5351,15 +5370,16 @@ class HttpCli(object):
|
|||
raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
|
||||
raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
|
||||
|
||||
zs = self.uparam.get("filter") or ""
|
||||
filt = re.compile(zs, re.I) if zs else None
|
||||
lm = "ups %r" % (zs,)
|
||||
sfilt = self.uparam.get("filter") or ""
|
||||
nfi, vfi = str_anchor(sfilt)
|
||||
lm = "ups %d%r" % (nfi, sfilt)
|
||||
|
||||
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
||||
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
||||
else:
|
||||
shr_dbv = None
|
||||
|
||||
wret: dict[str, Any] = {}
|
||||
ret: list[dict[str, Any]] = []
|
||||
t0 = time.time()
|
||||
lim = time.time() - self.args.unpost
|
||||
|
@ -5381,7 +5401,13 @@ class HttpCli(object):
|
|||
x = self.conn.hsrv.broker.ask(
|
||||
"up2k.get_unfinished_by_user", self.uname, "" if bad_xff else self.ip
|
||||
)
|
||||
uret = x.get()
|
||||
zdsa: dict[str, Any] = x.get()
|
||||
uret: list[dict[str, Any]] = []
|
||||
if "timeout" in zdsa:
|
||||
wret["nou"] = 1
|
||||
else:
|
||||
uret = zdsa["f"]
|
||||
nu = len(uret)
|
||||
|
||||
if not self.args.unpost:
|
||||
allvols = []
|
||||
|
@ -5406,8 +5432,14 @@ class HttpCli(object):
|
|||
q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
|
||||
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
||||
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
||||
if filt and not filt.search(vp):
|
||||
continue
|
||||
if nfi == 0 or (nfi == 1 and vfi in vp):
|
||||
pass
|
||||
elif nfi == 2:
|
||||
if not vp.startswith(vfi):
|
||||
continue
|
||||
elif nfi == 3:
|
||||
if not vp.endswith(vfi):
|
||||
continue
|
||||
|
||||
n -= 1
|
||||
if not n:
|
||||
|
@ -5427,6 +5459,8 @@ class HttpCli(object):
|
|||
|
||||
if len(ret) > 2000:
|
||||
ret = ret[:2000]
|
||||
if len(ret) >= 2000:
|
||||
wret["oc"] = 1
|
||||
|
||||
for rv in ret:
|
||||
rv["vp"] = quotep(rv["vp"])
|
||||
|
@ -5446,6 +5480,13 @@ class HttpCli(object):
|
|||
)
|
||||
rv["vp"] += "?k=" + fk[:nfk]
|
||||
|
||||
if not allvols:
|
||||
wret["noc"] = 1
|
||||
ret = []
|
||||
|
||||
nc = len(ret)
|
||||
ret = uret + ret
|
||||
|
||||
if shr_dbv:
|
||||
# translate vpaths from share-target to share-url
|
||||
# to satisfy access checks
|
||||
|
@ -5460,12 +5501,11 @@ class HttpCli(object):
|
|||
for v in ret:
|
||||
v["vp"] = self.args.SR + v["vp"]
|
||||
|
||||
if not allvols:
|
||||
ret = [{"kinshi": 1}]
|
||||
|
||||
jtxt = '{"u":%s,"c":%s}' % (uret, json.dumps(ret, separators=(",\n", ": ")))
|
||||
zi = len(uret.split('\n"pd":')) - 1
|
||||
self.log("%s #%d+%d %.2fsec" % (lm, zi, len(ret), time.time() - t0))
|
||||
wret["f"] = ret
|
||||
wret["nu"] = nu
|
||||
wret["nc"] = nc
|
||||
jtxt = json.dumps(wret, separators=(",\n", ": "))
|
||||
self.log("%s #%d+%d %.2fsec" % (lm, nu, nc, time.time() - t0))
|
||||
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||
return True
|
||||
|
||||
|
@ -5480,8 +5520,8 @@ class HttpCli(object):
|
|||
raise Pebkac(500, "server busy, cannot list recent uploads; please retry")
|
||||
|
||||
sfilt = self.uparam.get("filter") or ""
|
||||
filt = re.compile(sfilt, re.I) if sfilt else None
|
||||
lm = "ru %r" % (sfilt,)
|
||||
nfi, vfi = str_anchor(sfilt)
|
||||
lm = "ru %d%r" % (nfi, sfilt)
|
||||
self.log(lm)
|
||||
|
||||
ret: list[dict[str, Any]] = []
|
||||
|
@ -5516,8 +5556,14 @@ class HttpCli(object):
|
|||
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
||||
for sz, rd, fn, ip, at in cur.execute(q):
|
||||
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
||||
if filt and not filt.search(vp):
|
||||
continue
|
||||
if nfi == 0 or (nfi == 1 and vfi in vp):
|
||||
pass
|
||||
elif nfi == 2:
|
||||
if not vp.startswith(vfi):
|
||||
continue
|
||||
elif nfi == 3:
|
||||
if not vp.endswith(vfi):
|
||||
continue
|
||||
|
||||
if not dots and "/." in vp:
|
||||
continue
|
||||
|
@ -5591,7 +5637,7 @@ class HttpCli(object):
|
|||
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||
return True
|
||||
|
||||
html = self.j2s("rups", this=self, v=jtxt)
|
||||
html = self.j2s("rups", this=self, v=json_hesc(jtxt))
|
||||
self.reply(html.encode("utf-8"), status=200)
|
||||
return True
|
||||
|
||||
|
@ -5655,15 +5701,15 @@ class HttpCli(object):
|
|||
raise Pebkac(500, "sqlite3 not found on server; sharing is disabled")
|
||||
raise Pebkac(500, "server busy, cannot create share; please retry in a bit")
|
||||
|
||||
skey = self.uparam.get("skey") or self.vpath.split("/")[-1]
|
||||
|
||||
if self.args.shr_v:
|
||||
self.log("handle_eshare: " + self.req)
|
||||
self.log("handle_eshare: " + skey)
|
||||
|
||||
cur = idx.get_shr()
|
||||
if not cur:
|
||||
raise Pebkac(400, "huh, sharing must be disabled in the server config...")
|
||||
|
||||
skey = self.vpath.split("/")[-1]
|
||||
|
||||
rows = cur.execute("select un, t1 from sh where k = ?", (skey,)).fetchall()
|
||||
un = rows[0][0] if rows and rows[0] else ""
|
||||
|
||||
|
|
|
@ -320,7 +320,7 @@ class SMB(object):
|
|||
|
||||
self.hub.up2k.handle_mv(uname, "1.7.6.2", vp1, vp2)
|
||||
try:
|
||||
bos.makedirs(ap2, vfs2.flags["chmod_d"])
|
||||
bos.makedirs(ap2, vf=vfs2.flags)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ from .util import (
|
|||
HAVE_PSUTIL,
|
||||
HAVE_SQLITE3,
|
||||
HAVE_ZMQ,
|
||||
RE_ANSI,
|
||||
URL_BUG,
|
||||
UTC,
|
||||
VERSIONS,
|
||||
|
@ -60,7 +61,6 @@ from .util import (
|
|||
HMaccas,
|
||||
ODict,
|
||||
alltrace,
|
||||
ansi_re,
|
||||
build_netmap,
|
||||
expat_ver,
|
||||
gzip,
|
||||
|
@ -1026,6 +1026,8 @@ class SvcHub(object):
|
|||
except:
|
||||
raise Exception("invalid --mv-retry [%s]" % (self.args.mv_retry,))
|
||||
|
||||
al.js_utc = "false" if al.localtime else "true"
|
||||
|
||||
al.tcolor = al.tcolor.lstrip("#")
|
||||
if len(al.tcolor) == 3: # fc5 => ffcc55
|
||||
al.tcolor = "".join([x * 2 for x in al.tcolor])
|
||||
|
@ -1409,9 +1411,9 @@ class SvcHub(object):
|
|||
if self.no_ansi:
|
||||
fmt = "%s %-21s %s\n"
|
||||
if "\033" in msg:
|
||||
msg = ansi_re.sub("", msg)
|
||||
msg = RE_ANSI.sub("", msg)
|
||||
if "\033" in src:
|
||||
src = ansi_re.sub("", src)
|
||||
src = RE_ANSI.sub("", src)
|
||||
elif c:
|
||||
if isinstance(c, int):
|
||||
msg = "\033[3%sm%s\033[0m" % (c, msg)
|
||||
|
|
|
@ -45,6 +45,7 @@ from .util import (
|
|||
exclude_dotfiles,
|
||||
min_ex,
|
||||
runhook,
|
||||
set_fperms,
|
||||
undot,
|
||||
vjoin,
|
||||
vsplit,
|
||||
|
@ -388,8 +389,8 @@ class Tftpd(object):
|
|||
a = (self.args.iobuf,)
|
||||
|
||||
ret = open(ap, mode, *a, **ka)
|
||||
if wr and "chmod_f" in vfs.flags:
|
||||
os.fchmod(ret.fileno(), vfs.flags["chmod_f"])
|
||||
if wr and "fperms" in vfs.flags:
|
||||
set_fperms(ret, vfs.flags)
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -398,7 +399,9 @@ class Tftpd(object):
|
|||
if "*" not in vfs.axs.uwrite:
|
||||
yeet("blocked mkdir; folder not world-writable: /%s" % (vpath,))
|
||||
|
||||
return bos.mkdir(ap, vfs.flags["chmod_d"])
|
||||
bos.mkdir(ap, vfs.flags["chmod_d"])
|
||||
if "chown" in vfs.flags:
|
||||
bos.chown(ap, vfs.flags["uid"], vfs.flags["gid"])
|
||||
|
||||
def _unlink(self, vpath: str) -> None:
|
||||
# return bos.unlink(self._v2a("stat", vpath, *a)[1])
|
||||
|
|
|
@ -269,8 +269,8 @@ class ThumbSrv(object):
|
|||
self.log("joined waiting room for %r" % (tpath,))
|
||||
except:
|
||||
thdir = os.path.dirname(tpath)
|
||||
chmod = 0o700 if self.args.free_umask else 0o755
|
||||
bos.makedirs(os.path.join(thdir, "w"), chmod)
|
||||
chmod = bos.MKD_700 if self.args.free_umask else bos.MKD_755
|
||||
bos.makedirs(os.path.join(thdir, "w"), vf=chmod)
|
||||
|
||||
inf_path = os.path.join(thdir, "dir.txt")
|
||||
if not bos.path.exists(inf_path):
|
||||
|
|
|
@ -399,12 +399,14 @@ class Up2k(object):
|
|||
|
||||
return "{}"
|
||||
|
||||
def get_unfinished_by_user(self, uname, ip) -> str:
|
||||
def get_unfinished_by_user(self, uname, ip) -> dict[str, Any]:
|
||||
# returns dict due to ExceptionalQueue
|
||||
if PY2 or not self.reg_mutex.acquire(timeout=2):
|
||||
return '[{"timeout":1}]'
|
||||
return {"timeout": 1}
|
||||
|
||||
ret: list[tuple[int, str, int, int, int]] = []
|
||||
userset = set([(uname or "\n"), "*"])
|
||||
n = 1000
|
||||
try:
|
||||
for ptop, tab2 in self.registry.items():
|
||||
cfg = self.flags.get(ptop, {}).get("u2abort", 1)
|
||||
|
@ -419,7 +421,6 @@ class Up2k(object):
|
|||
or (addr and addr != job["addr"])
|
||||
):
|
||||
continue
|
||||
|
||||
zt5 = (
|
||||
int(job["t0"]),
|
||||
djoin(job["vtop"], job["prel"], job["name"]),
|
||||
|
@ -428,6 +429,9 @@ class Up2k(object):
|
|||
len(job["hash"]),
|
||||
)
|
||||
ret.append(zt5)
|
||||
n -= 1
|
||||
if not n:
|
||||
break
|
||||
finally:
|
||||
self.reg_mutex.release()
|
||||
|
||||
|
@ -444,7 +448,7 @@ class Up2k(object):
|
|||
}
|
||||
for (at, vp, sz, nn, nh) in ret
|
||||
]
|
||||
return json.dumps(ret2, separators=(",\n", ": "))
|
||||
return {"f": ret2}
|
||||
|
||||
def get_unfinished(self) -> str:
|
||||
if PY2 or not self.reg_mutex.acquire(timeout=0.5):
|
||||
|
@ -916,7 +920,7 @@ class Up2k(object):
|
|||
for vol in vols:
|
||||
try:
|
||||
# mkdir gonna happen at snap anyways;
|
||||
bos.makedirs(vol.realpath, vol.flags["chmod_d"])
|
||||
bos.makedirs(vol.realpath, vf=vol.flags)
|
||||
dir_is_empty(self.log_func, not self.args.no_scandir, vol.realpath)
|
||||
except Exception as ex:
|
||||
self.volstate[vol.vpath] = "OFFLINE (cannot access folder)"
|
||||
|
@ -2827,7 +2831,7 @@ class Up2k(object):
|
|||
# v5a -> v5b
|
||||
# store rd+fn rather than warks to support nohash vols
|
||||
try:
|
||||
cur.execute("select ws, rd, fn from iu limit 1").fetchone()
|
||||
cur.execute("select c, w, rd, fn from iu limit 1").fetchone()
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
@ -3309,7 +3313,7 @@ class Up2k(object):
|
|||
reg,
|
||||
"up2k._get_volsize",
|
||||
)
|
||||
bos.makedirs(ap2, vfs.flags["chmod_d"])
|
||||
bos.makedirs(ap2, vf=vfs.flags)
|
||||
vfs.lim.nup(cj["addr"])
|
||||
vfs.lim.bup(cj["addr"], cj["size"])
|
||||
|
||||
|
@ -3445,7 +3449,7 @@ class Up2k(object):
|
|||
"wb",
|
||||
fdir=fdir,
|
||||
suffix="-%.6f-%s" % (ts, dip),
|
||||
chmod=vf.get("chmod_f", -1),
|
||||
vf=vf,
|
||||
)
|
||||
f.close()
|
||||
return ret
|
||||
|
@ -4304,7 +4308,7 @@ class Up2k(object):
|
|||
self.log(t, 1)
|
||||
raise Pebkac(405, t)
|
||||
|
||||
bos.makedirs(os.path.dirname(dabs), dvn.flags["chmod_d"])
|
||||
bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
|
||||
|
||||
c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(
|
||||
svn_dbv.realpath, srem_dbv
|
||||
|
@ -4480,7 +4484,10 @@ class Up2k(object):
|
|||
vp = vjoin(dvp, rem)
|
||||
try:
|
||||
dvn, drem = self.vfs.get(vp, uname, False, True)
|
||||
bos.mkdir(dvn.canonical(drem), dvn.flags["chmod_d"])
|
||||
dap = dvn.canonical(drem)
|
||||
bos.mkdir(dap, dvn.flags["chmod_d"])
|
||||
if "chown" in dvn.flags:
|
||||
bos.chown(dap, dvn.flags["uid"], dvn.flags["gid"])
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -4550,7 +4557,7 @@ class Up2k(object):
|
|||
|
||||
is_xvol = svn.realpath != dvn.realpath
|
||||
|
||||
bos.makedirs(os.path.dirname(dabs), dvn.flags["chmod_d"])
|
||||
bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
|
||||
|
||||
if is_dirlink:
|
||||
dlabs = absreal(sabs)
|
||||
|
@ -5062,7 +5069,7 @@ class Up2k(object):
|
|||
"wb",
|
||||
fdir=pdir,
|
||||
suffix="-%.6f-%s" % (job["t0"], dip),
|
||||
chmod=vf.get("chmod_f", -1),
|
||||
vf=vf,
|
||||
)
|
||||
try:
|
||||
abspath = djoin(pdir, job["tnam"])
|
||||
|
|
|
@ -155,7 +155,9 @@ except:
|
|||
HAVE_PSUTIL = False
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_MAGIC"):
|
||||
if os.environ.get("PRTY_NO_MAGIC") or (
|
||||
ANYWIN and not os.environ.get("PRTY_FORCE_MAGIC")
|
||||
):
|
||||
raise Exception()
|
||||
|
||||
import magic
|
||||
|
@ -241,7 +243,18 @@ except:
|
|||
BITNESS = struct.calcsize("P") * 8
|
||||
|
||||
|
||||
ansi_re = re.compile("\033\\[[^mK]*[mK]")
|
||||
RE_ANSI = re.compile("\033\\[[^mK]*[mK]")
|
||||
RE_HTML_SH = re.compile(r"[<>&$?`\"';]")
|
||||
RE_CTYPE = re.compile(r"^content-type: *([^; ]+)", re.IGNORECASE)
|
||||
RE_CDISP = re.compile(r"^content-disposition: *([^; ]+)", re.IGNORECASE)
|
||||
RE_CDISP_FIELD = re.compile(
|
||||
r'^content-disposition:(?: *|.*; *)name="([^"]+)"', re.IGNORECASE
|
||||
)
|
||||
RE_CDISP_FILE = re.compile(
|
||||
r'^content-disposition:(?: *|.*; *)filename="(.*)"', re.IGNORECASE
|
||||
)
|
||||
RE_MEMTOTAL = re.compile("^MemTotal:.* kB")
|
||||
RE_MEMAVAIL = re.compile("^MemAvailable:.* kB")
|
||||
|
||||
|
||||
BOS_SEP = ("%s" % (os.sep,)).encode("ascii")
|
||||
|
@ -486,11 +499,11 @@ def read_ram() -> tuple[float, float]:
|
|||
with open("/proc/meminfo", "rb", 0x10000) as f:
|
||||
zsl = f.read(0x10000).decode("ascii", "replace").split("\n")
|
||||
|
||||
p = re.compile("^MemTotal:.* kB")
|
||||
p = RE_MEMTOTAL
|
||||
zs = next((x for x in zsl if p.match(x)))
|
||||
a = int((int(zs.split()[1]) / 0x100000) * 100) / 100
|
||||
|
||||
p = re.compile("^MemAvailable:.* kB")
|
||||
p = RE_MEMAVAIL
|
||||
zs = next((x for x in zsl if p.match(x)))
|
||||
b = int((int(zs.split()[1]) / 0x100000) * 100) / 100
|
||||
except:
|
||||
|
@ -1585,7 +1598,8 @@ def ren_open(fname: str, *args: Any, **kwargs: Any) -> tuple[typing.IO[Any], str
|
|||
fun = kwargs.pop("fun", open)
|
||||
fdir = kwargs.pop("fdir", None)
|
||||
suffix = kwargs.pop("suffix", None)
|
||||
chmod = kwargs.pop("chmod", -1)
|
||||
vf = kwargs.pop("vf", None)
|
||||
fperms = vf and "fperms" in vf
|
||||
|
||||
if fname == os.devnull:
|
||||
return fun(fname, *args, **kwargs), fname
|
||||
|
@ -1629,11 +1643,11 @@ def ren_open(fname: str, *args: Any, **kwargs: Any) -> tuple[typing.IO[Any], str
|
|||
fp2 = os.path.join(fdir, fp2)
|
||||
with open(fsenc(fp2), "wb") as f2:
|
||||
f2.write(orig_name.encode("utf-8"))
|
||||
if chmod >= 0:
|
||||
os.fchmod(f2.fileno(), chmod)
|
||||
if fperms:
|
||||
set_fperms(f2, vf)
|
||||
|
||||
if chmod >= 0:
|
||||
os.fchmod(f.fileno(), chmod)
|
||||
if fperms:
|
||||
set_fperms(f, vf)
|
||||
|
||||
return f, fname
|
||||
|
||||
|
@ -1695,14 +1709,10 @@ class MultipartParser(object):
|
|||
self.args = args
|
||||
self.headers = http_headers
|
||||
|
||||
self.re_ctype = re.compile(r"^content-type: *([^; ]+)", re.IGNORECASE)
|
||||
self.re_cdisp = re.compile(r"^content-disposition: *([^; ]+)", re.IGNORECASE)
|
||||
self.re_cdisp_field = re.compile(
|
||||
r'^content-disposition:(?: *|.*; *)name="([^"]+)"', re.IGNORECASE
|
||||
)
|
||||
self.re_cdisp_file = re.compile(
|
||||
r'^content-disposition:(?: *|.*; *)filename="(.*)"', re.IGNORECASE
|
||||
)
|
||||
self.re_ctype = RE_CTYPE
|
||||
self.re_cdisp = RE_CDISP
|
||||
self.re_cdisp_field = RE_CDISP_FIELD
|
||||
self.re_cdisp_file = RE_CDISP_FILE
|
||||
|
||||
self.boundary = b""
|
||||
self.gen: Optional[
|
||||
|
@ -2244,6 +2254,16 @@ def find_prefix(ips: list[str], cidrs: list[str]) -> list[str]:
|
|||
return ret
|
||||
|
||||
|
||||
def html_sh_esc(s: str) -> str:
|
||||
s = re.sub(RE_HTML_SH, "_", s).replace(" ", "%20")
|
||||
s = s.replace("\r", "_").replace("\n", "_")
|
||||
return s
|
||||
|
||||
|
||||
def json_hesc(s: str) -> str:
|
||||
return s.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026")
|
||||
|
||||
|
||||
def html_escape(s: str, quot: bool = False, crlf: bool = False) -> str:
|
||||
"""html.escape but also newlines"""
|
||||
s = s.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
|
@ -2376,6 +2396,21 @@ def ujoin(rd: str, fn: str) -> str:
|
|||
return rd or fn
|
||||
|
||||
|
||||
def str_anchor(txt) -> tuple[int, str]:
|
||||
if not txt:
|
||||
return 0, ""
|
||||
txt = txt.lower()
|
||||
a = txt.startswith("^")
|
||||
b = txt.endswith("$")
|
||||
if not b:
|
||||
if not a:
|
||||
return 1, txt # ~
|
||||
return 2, txt[1:] # ^
|
||||
if not a:
|
||||
return 3, txt[:-1] # $
|
||||
return 4, txt[1:-1] # ^$
|
||||
|
||||
|
||||
def log_reloc(
|
||||
log: "NamedLogger",
|
||||
re: dict[str, str],
|
||||
|
@ -2563,6 +2598,14 @@ def lsof(log: "NamedLogger", abspath: str) -> None:
|
|||
log("lsof failed; " + min_ex(), 3)
|
||||
|
||||
|
||||
def set_fperms(f: Union[typing.BinaryIO, typing.IO[Any]], vf: dict[str, Any]) -> None:
|
||||
fno = f.fileno()
|
||||
if "chmod_f" in vf:
|
||||
os.fchmod(fno, vf["chmod_f"])
|
||||
if "chown" in vf:
|
||||
os.fchown(fno, vf["uid"], vf["gid"])
|
||||
|
||||
|
||||
def _fs_mvrm(
|
||||
log: "NamedLogger", src: str, dst: str, atomic: bool, flags: dict[str, Any]
|
||||
) -> bool:
|
||||
|
@ -4169,7 +4212,12 @@ def load_resource(E: EnvParams, name: str, mode="rb") -> IO[bytes]:
|
|||
stream = codecs.getreader(enc)(stream)
|
||||
return stream
|
||||
|
||||
return open(os.path.join(E.mod, name), mode, encoding=enc)
|
||||
ap = os.path.join(E.mod, name)
|
||||
|
||||
if PY2:
|
||||
return codecs.open(ap, "r", encoding=enc) # type: ignore
|
||||
|
||||
return open(ap, mode, encoding=enc)
|
||||
|
||||
|
||||
class Pebkac(Exception):
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
{%- for f in files %}
|
||||
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td>
|
||||
{%- if f.tags is defined %}
|
||||
{%- for k in taglist %}<td>{{ f.tags[k] }}</td>{%- endfor %}
|
||||
{%- for k in taglist %}<td>{{ f.tags[k]|e }}</td>{%- endfor %}
|
||||
{%- endif %}<td>{{ f.ext }}</td><td>{{ f.dt }}</td></tr>
|
||||
{%- endfor %}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -33,6 +33,7 @@
|
|||
|
||||
var SR="{{ r }}",
|
||||
lang="{{ lang }}",
|
||||
dutc={{ this.args.js_utc }},
|
||||
dfavico="{{ favico }}";
|
||||
|
||||
var STG = window.localStorage;
|
||||
|
|
|
@ -10,7 +10,7 @@ function render() {
|
|||
fn = esc(uricom_dec(vsp[1])),
|
||||
at = f.at,
|
||||
td = now - f.at,
|
||||
ts = !at ? '(?)' : unix2iso(at),
|
||||
ts = !at ? '(?)' : unix2ui(at),
|
||||
sa = !at ? '(?)' : td > 60 ? shumantime(td) : (td + 's'),
|
||||
sz = ('' + f.sz).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
var SR="{{ r }}",
|
||||
shr="{{ shr }}",
|
||||
lang="{{ lang }}",
|
||||
dutc={{ this.args.js_utc }},
|
||||
dfavico="{{ favico }}";
|
||||
|
||||
var STG = window.localStorage;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
var SRS = SR.trimEnd('/') + '/';
|
||||
|
||||
var t = QSA('a[k]');
|
||||
for (var a = 0; a < t.length; a++)
|
||||
t[a].onclick = rm;
|
||||
|
||||
function rm() {
|
||||
var u = SR + shr + uricom_enc(this.getAttribute('k')) + '?eshare=rm',
|
||||
var u = SRS + '?eshare=rm&skey=' + uricom_enc(this.getAttribute('k')),
|
||||
xhr = new XHR();
|
||||
|
||||
xhr.open('POST', u, true);
|
||||
|
@ -13,7 +15,7 @@ function rm() {
|
|||
|
||||
function bump() {
|
||||
var k = this.closest('tr').getElementsByTagName('a')[2].getAttribute('k'),
|
||||
u = SR + shr + uricom_enc(k) + '?eshare=' + this.value,
|
||||
u = SRS + '?skey=' + uricom_enc(k) + '&eshare=' + this.value,
|
||||
xhr = new XHR();
|
||||
|
||||
xhr.open('POST', u, true);
|
||||
|
@ -64,7 +66,7 @@ function showqr(href) {
|
|||
for (var b = 7; b < 9; b++) {
|
||||
var v = buf[ibuf++];
|
||||
tr[a].cells[b].innerHTML =
|
||||
v ? unix2iso(v).replace(' ', ', ') : 'never';
|
||||
v ? unix2ui(v).replace(' ', ', ') : 'never';
|
||||
}
|
||||
|
||||
for (var a = 0; a < tr.length; a++)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// please add translations in alphabetic order, but keep "nor" and "eng" first
|
||||
var Ls = {
|
||||
"nor": {
|
||||
"a1": "oppdater",
|
||||
|
@ -50,7 +51,6 @@ var Ls = {
|
|||
"ta2": "repeat to confirm new password:",
|
||||
"ta3": "found a typo; please try again",
|
||||
},
|
||||
|
||||
"chi": {
|
||||
"a1": "更新",
|
||||
"b1": "你好 <small>(你尚未登录)</small>",
|
||||
|
@ -92,7 +92,174 @@ var Ls = {
|
|||
"ae1": "正在下载:", //m
|
||||
"af1": "显示最近上传的文件", //m
|
||||
"ag1": "查看已知 IdP 用户", //m
|
||||
}
|
||||
},
|
||||
"deu": {
|
||||
"a1": "Neu laden",
|
||||
"b1": "Tach, wie geht's? <small>(Du bist nicht angemeldet)</small>",
|
||||
"c1": "Abmelden",
|
||||
"d1": "Zustand",
|
||||
"d2": "Zeigt den Zustand aller aktiven Threads",
|
||||
"e1": "Config neu laden",
|
||||
"e2": "Konfigurationsdatei neu laden (Accounts/Volumes/VolFlags)$Nund scannt alle e2ds-Volumes$N$NBeachte: Jegliche Änderung an globalen Einstellungen$Nbenötigt einen Neustart zum Anwenden",
|
||||
"f1": "Du kannst lesen:",
|
||||
"g1": "Du kannst hochladen nach:",
|
||||
"cc1": "Andere Dinge:",
|
||||
"h1": "k304 deaktivieren",
|
||||
"i1": "k304 aktivieren",
|
||||
"j1": "k304 trennt die Clientverbindung bei jedem HTTP 304, was Bugs mit problematischen Proxies vorbeugen kann (z.B. nicht ladenden Seiten), macht Dinge aber generell langsamer",
|
||||
"k1": "Client-Einstellungen zurücksetzen",
|
||||
"l1": "Melde dich an für mehr:",
|
||||
"m1": "Willkommen zurück,",
|
||||
"n1": "404 Nicht gefunden ┐( ´ -`)┌",
|
||||
"o1": 'or maybe you don\'t have access -- try a password or <a href="' + SR + '/?h">go home</a>',
|
||||
"p1": "403 Verboten ~┻━┻",
|
||||
"q1": 'Benutze ein Passwort oder <a href="' + SR + '/?h">gehe zur Homepage</a>',
|
||||
"r1": "Gehe zur Homepage",
|
||||
".s1": "Neu scannen",
|
||||
"t1": "Aktion",
|
||||
"u2": "time since the last server write$N( upload / rename / ... )$N$N17d = 17 days$N1h23 = 1 hour 23 minutes$N4m56 = 4 minutes 56 seconds",
|
||||
"v1": "Verbinden",
|
||||
"v2": "Benutze diesen Server als lokale Festplatte",
|
||||
"w1": "Zu HTTPS wechseln",
|
||||
"x1": "Passwort ändern",
|
||||
"y1": "Shares bearbeiten",
|
||||
"z1": "Share entsperren:",
|
||||
"ta1": "Trage zuerst dein Passwort ein",
|
||||
"ta2": "Wiederhole dein Passwort zur Bestätigung:",
|
||||
"ta3": "Da stimmt etwas nicht; probier's nochmal",
|
||||
"aa1": "Eingehende Dateien:",
|
||||
"ab1": "no304 deaktivieren",
|
||||
"ac1": "no304 aktivieren",
|
||||
"ad1": "Das Aktivieren von no304 deaktiviert jegliche Form von Caching; probier dies, wenn k304 nicht genug war. Dies verschwendet eine grosse Menge Netzwerk-Traffic!",
|
||||
"ae1": "Aktive Downloads:",
|
||||
"af1": "Zeige neue Uploads",
|
||||
},
|
||||
"fin": {
|
||||
"a1": "päivitä",
|
||||
"b1": "hei sie muukalainen <small>(et ole kirjautunut sisään)</small>",
|
||||
"c1": "kirjaudu ulos",
|
||||
"d1": "tulosta pinojälki",
|
||||
"d2": "näytä kaikkien aktiivisten säikeiden tila",
|
||||
"e1": "päivitä konffit",
|
||||
"e2": "lataa konfiguraatiotiedostot uudelleen (käyttäjätilit/asemat/asemaflagit),$Nja skannaa kaikki e2ds asemat uudelleen$N$Nhuom: kaikki global-asetuksiin$Ntehdyt muutokset vaativat täyden$Nuudelleenkäynnistyksen",
|
||||
"f1": "voit selata:",
|
||||
"g1": "voit ladata:",
|
||||
"cc1": "muuta:",
|
||||
"h1": "poista k304 käytöstä",
|
||||
"i1": "ota k304 käyttöön",
|
||||
"j1": "k304 katkaisee yhteytesi jokaisella HTTP 304:llä, mikä voi estää joitain bugisia välityspalvelimia jumittumasta/lopettamasta sivujen lataamista, <em>mutta</em> se myös vähentää suorituskykyä",
|
||||
"k1": "nollaa asetukset",
|
||||
"l1": "kirjaudu sisään:",
|
||||
"m1": "tervetuloa takaisin,",
|
||||
"n1": "404: ei löytynyt mitään ┐( ´ -`)┌",
|
||||
"o1": 'tai ehkä sinulla ei vain ole käyttöoikeuksia? kokeile salasanaa tai <a href="' + SR + '/?h">mene kotiin</a>',
|
||||
"p1": "403: pääsy kielletty ~┻━┻",
|
||||
"q1": 'kokeile salasanaa tai <a href="' + SR + '/?h">mene kotiin</a>',
|
||||
"r1": "mene kotiin",
|
||||
".s1": "uudelleenkartoita",
|
||||
"t1": "toiminto",
|
||||
"u2": "aika viimeisestä palvelimen kirjoituksesta$N( lataus / uudelleennimeäminen / tms. )$N$N17d = 17 päivää$N1h23 = 1 tunti 23 minuuttia$N4m56 = 4 minuuttia 56 sekuntia",
|
||||
"v1": "yhdistä",
|
||||
"v2": "käytä tätä palvelinta paikallisena kiintolevynä",
|
||||
"w1": "vaihda https:ään",
|
||||
"x1": "vaihda salasana",
|
||||
"y1": "muokkaa jakoja",
|
||||
"z1": "avaa tämä jako:",
|
||||
"ta1": "täytä ensin uusi salasana",
|
||||
"ta2": "toista vahvistaaksesi uuden salasanan:",
|
||||
"ta3": "löytyi kirjoitusvirhe; yritä uudelleen",
|
||||
"aa1": "saapuvat:",
|
||||
"ab1": "poista no304 käytöstä",
|
||||
"ac1": "ota no304 käyttöön",
|
||||
"ad1": "no304:n lopettaa välimuistin käytön kokonaan; kokeile tätä jos k304 ei riittänyt. Tuhlaa valtavan määrän verkkoliikennettä!",
|
||||
"ae1": "lähtevät:",
|
||||
"af1": "näytä viimeaikaiset lataukset",
|
||||
"ag1": "näytä tunnetut IdP-käyttäjät",
|
||||
},
|
||||
"spa": {
|
||||
"a1": "actualizar",
|
||||
"b1": "hola <small>(no has iniciado sesión)</small>",
|
||||
"c1": "cerrar sesión",
|
||||
"d1": "volcar estado de la pila",
|
||||
"d2": "muestra el estado de todos los hilos activos",
|
||||
"e1": "recargar configuración",
|
||||
"e2": "recargar archivos de configuración (cuentas/volúmenes/indicadores de vol.),$Ny reescanear todos los volúmenes e2ds$N$Nnota: cualquier cambio en la configuración global$Nrequiere un reinicio completo para surtir efecto",
|
||||
"f1": "puedes explorar:",
|
||||
"g1": "puedes subir a:",
|
||||
"cc1": "otras cosas:",
|
||||
"h1": "desactivar k304",
|
||||
"i1": "activar k304",
|
||||
"j1": "activar k304 desconectará tu cliente en cada HTTP 304, lo que puede evitar que algunos proxies con errores se atasquen (dejando de cargar páginas de repente), <em>pero</em> también ralentizará las cosas en general",
|
||||
"k1": "restablecer config. de cliente",
|
||||
"l1": "inicia sesión para más:",
|
||||
"m1": "bienvenido de nuevo,",
|
||||
"n1": "404 no encontrado ┐( ´ -`)┌",
|
||||
"o1": '¿o quizás no tienes acceso? -- prueba con una contraseña o <a href=\"' + SR + '/?h\">vuelve al inicio</a>',
|
||||
"p1": "403 prohibido ~┻━┻",
|
||||
"q1": 'usa una contraseña o <a href=\"' + SR + '/?h\">vuelve al inicio</a>',
|
||||
"r1": "ir al inicio",
|
||||
".s1": "reescanear",
|
||||
"t1": "acción",
|
||||
"u2": "tiempo desde la última escritura en el servidor$N( subida / renombrar / ... )$N$N17d = 17 días$N1h23 = 1 hora 23 minutos$N4m56 = 4 minutos 56 segundos",
|
||||
"v1": "conectar",
|
||||
"v2": "usar este servidor como un disco duro local",
|
||||
"w1": "cambiar a https",
|
||||
"x1": "cambiar contraseña",
|
||||
"y1": "editar recursos compartidos",
|
||||
"z1": "desbloquear este recurso compartido:",
|
||||
"ta1": "primero escribe tu nueva contraseña",
|
||||
"ta2": "repite para confirmar la nueva contraseña:",
|
||||
"ta3": "hay un error; por favor, inténtalo de nuevo",
|
||||
"aa1": "archivos entrantes:",
|
||||
"ab1": "desactivar no304",
|
||||
"ac1": "activar no304",
|
||||
"ad1": "activar no304 desactivará todo el almacenamiento en caché; prueba esto si k304 no fue suficiente. ¡Esto desperdiciará una gran cantidad de tráfico de red!",
|
||||
"ae1": "descargas activas:",
|
||||
"af1": "mostrar subidas recientes",
|
||||
"ag1": "mostrar usuarios IdP conocidos"
|
||||
},
|
||||
"rus": {
|
||||
"a1": "обновить",
|
||||
"b1": "приветик, незнакомец <small>(вы не авторизованы)</small>",
|
||||
"c1": "выйти",
|
||||
"d1": "трассировка стека",
|
||||
"d2": "показывает состояние всех активных потоков",
|
||||
"e1": "перезагрузить конфиг",
|
||||
"e2": "перезагрузить файлы конфига (аккаунты/хранилища/флаги),$Nи пересканировать все хранилища с флагом e2ds$N$Nвнимание: изменения глобальных настроек$Nтребуют полного перезапуска сервера",
|
||||
"f1": "вы можете видеть:",
|
||||
"g1": "вы можете загружать файлы в:",
|
||||
"cc1": "всякая всячина:",
|
||||
"h1": "отключить k304",
|
||||
"i1": "включить k304",
|
||||
"j1": "включённый k304 будет отключать вас при получении HTTP 304, что может помочь при работе с некоторыми глючными прокси (перестают загружаться страницы), <em>но</em> это также сделает работу клиента медленнее",
|
||||
"k1": "сбросить локальные настройки",
|
||||
"l1": "авторизуйтесь для других опций:",
|
||||
"m1": "с возвращением,",
|
||||
"n1": "404 не найдено ┐( ´ -`)┌",
|
||||
"o1": 'или у вас нет доступа -- попробуйте авторизоваться или <a href="' + SR + '/?h">вернуться на главную</a>',
|
||||
"p1": "403 доступ запрещён ~┻━┻",
|
||||
"q1": 'авторизуйтесь или <a href="' + SR + '/?h">вернитесь на главную</a>',
|
||||
"r1": "вернуться на главную",
|
||||
".s1": "пересканировать",
|
||||
"t1": "действия",
|
||||
"u2": "время с последней записи на сервер$N( загрузка / переименование / ... )$N$N17d = 17 дней$N1h23 = 1 час 23 минут$N4m56 = 4 минут 56 секунд",
|
||||
"v1": "подключить",
|
||||
"v2": "использовать сервер как локальный диск",
|
||||
"w1": "перейти на https",
|
||||
"x1": "поменять пароль",
|
||||
"y1": "управление доступом",
|
||||
"z1": "разблокировать:",
|
||||
"ta1": "сначала введите свой новый пароль",
|
||||
"ta2": "повторите новый пароль:",
|
||||
"ta3": "опечатка; попробуйте снова",
|
||||
"aa1": "входящие файлы:",
|
||||
"ab1": "отключить no304",
|
||||
"ac1": "включить no304",
|
||||
"ad1": "включённый no304 полностью отключит хеширование; используйте, если k304 не помог. Сильно увеличит объём трафика!",
|
||||
"ae1": "активные скачивания:",
|
||||
"af1": "показать недавние загрузки",
|
||||
"ag1": "показать известных IdP-пользователей",
|
||||
},
|
||||
};
|
||||
|
||||
if (window.langmod)
|
||||
|
|
|
@ -240,14 +240,26 @@
|
|||
<div class="os win">
|
||||
<h1>ShareX</h1>
|
||||
|
||||
<p>to upload screenshots using ShareX <a href="https://github.com/ShareX/ShareX/releases/tag/v12.1.1">v12</a> or <a href="https://getsharex.com/">v15+</a>, save this as <code>copyparty.sxcu</code> and run it:</p>
|
||||
<p>to upload screenshots using ShareX <a href="https://getsharex.com/">v15+</a>, save this as <code>copyparty.sxcu</code> and run it:</p>
|
||||
|
||||
<pre class="dl" name="copyparty.sxcu">
|
||||
{ "Version": "15.0.0", "Name": "copyparty",
|
||||
"RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}",
|
||||
"Headers": {
|
||||
{% if accs %}"pw": "<b>{{ pw }}</b>", {% endif %}"accept": "url"
|
||||
},
|
||||
"DestinationType": "ImageUploader, TextUploader, FileUploader",
|
||||
"Body": "MultipartFormData", "URL": "{response}",
|
||||
"RequestMethod": "POST", "FileFormName": "f" }
|
||||
</pre>
|
||||
|
||||
<p>for ShareX <a href="https://github.com/ShareX/ShareX/releases/tag/v12.1.1">v12</a> specifically, save this as <code>copyparty.sxcu</code> and run it:</p>
|
||||
|
||||
<pre class="dl" name="copyparty.sxcu">
|
||||
{ "Name": "copyparty",
|
||||
"RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}",
|
||||
"Headers": {
|
||||
{% if accs %}"pw": "<b>{{ pw }}</b>",{% endif %}
|
||||
"accept": "url"
|
||||
{% if accs %}"pw": "<b>{{ pw }}</b>", {% endif %}"accept": "url"
|
||||
},
|
||||
"DestinationType": "ImageUploader, TextUploader, FileUploader",
|
||||
"FileFormName": "f" }
|
||||
|
|
|
@ -2511,8 +2511,8 @@ function up2k_init(subtle) {
|
|||
var msg = [];
|
||||
for (var a = 0, aa = Math.min(20, response.hits.length); a < aa; a++) {
|
||||
var hit = response.hits[a],
|
||||
tr = unix2iso(hit.ts),
|
||||
tu = unix2iso(t.lmod),
|
||||
tr = unix2ui(hit.ts),
|
||||
tu = unix2ui(t.lmod),
|
||||
diff = parseInt(t.lmod) - parseInt(hit.ts),
|
||||
cdiff = (Math.abs(diff) <= 2) ? '3c0' : 'f0b',
|
||||
sdiff = '<span style="color:#' + cdiff + '">diff ' + diff;
|
||||
|
@ -3187,7 +3187,7 @@ function up2k_init(subtle) {
|
|||
return;
|
||||
|
||||
try {
|
||||
ebi('lifew').innerHTML = unix2iso((st.lifetime || lifetime) +
|
||||
ebi('lifew').innerHTML = unix2ui((st.lifetime || lifetime) +
|
||||
Date.now() / 1000 - new Date().getTimezoneOffset() * 60
|
||||
).replace(' ', ', ').slice(0, -3);
|
||||
}
|
||||
|
|
|
@ -907,11 +907,29 @@ function noq_href(el) {
|
|||
}
|
||||
|
||||
|
||||
function pad2(v) {
|
||||
return ('0' + v).slice(-2);
|
||||
}
|
||||
|
||||
|
||||
function unix2iso(ts) {
|
||||
return new Date(ts * 1000).toISOString().replace("T", " ").slice(0, -5);
|
||||
}
|
||||
|
||||
|
||||
function unix2iso_localtime(ts) {
|
||||
var o = new Date(ts * 1000),
|
||||
p = pad2;
|
||||
return "{0}-{1}-{2} {3}:{4}:{5}".format(
|
||||
o.getFullYear(),
|
||||
p(o.getMonth() + 1),
|
||||
p(o.getDate()),
|
||||
p(o.getHours()),
|
||||
p(o.getMinutes()),
|
||||
p(o.getSeconds()));
|
||||
}
|
||||
|
||||
|
||||
function s2ms(s) {
|
||||
s = Math.floor(s);
|
||||
var m = Math.floor(s / 60);
|
||||
|
@ -1203,6 +1221,13 @@ function scfg_bind(obj, oname, cname, defval, cb) {
|
|||
}
|
||||
|
||||
|
||||
window.unix2ui = (function () {
|
||||
var v = sread('utctid');
|
||||
v = v ? (v === '0') : (window.dutc === false);
|
||||
return v ? unix2iso_localtime : unix2iso;
|
||||
})();
|
||||
|
||||
|
||||
function hist_push(url) {
|
||||
console.log("h-push " + url);
|
||||
try {
|
||||
|
|
|
@ -1,3 +1,81 @@
|
|||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0731-0833 `v1.18.8` sfx hotfix
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #354 fix `copyparty-sfx.py` failing to start on certain versions of python c17ce4892ecdb4e11437bc2785d132bd8100eaec
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0730-2131 `v1.18.7` SECURITY: fix another XSS
|
||||
|
||||
## ⚠️ ATTN: this release fixes an XSS vulnerability
|
||||
|
||||
[GHSA-8mx2-rjh8-q3jq](https://github.com/9001/copyparty/security/advisories/GHSA-8mx2-rjh8-q3jq), could let an attacker execute arbitrary JS by tricking you into clicking a malicious URL
|
||||
|
||||
Soon there won't be many of these left, surely. Huge thanks to @Ju0x for finding and reporting this.
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #265 uid/gid for new files can be configured per-volume f1959988
|
||||
* has preconditions; [see readme](https://github.com/9001/copyparty#chmod-and-chown)
|
||||
* #212 add German translation (thx @rGunti, @Scotsguy, @chocolateimage) 9d32564c
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* GHSA-8mx2-rjh8-q3jq a8705e61
|
||||
* #276 windows: fix segfault (thx @kernel1994 for debugging!) a9d07c63
|
||||
* #272 webdav: send disk-size and disk-free to clients 4988a55e
|
||||
* #285 use disk-free sans root-reserve on linux (thx @Arklaum!) c3cc2dde
|
||||
* cors-check was funky on IPv6 e9684d40
|
||||
* #325 upgrade sharex example for newer versions 6016ec93
|
||||
* #300 restore support for old versions of python 2.7 b7ca6f4a
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* shares: the config POST-target is now always the webroot (for ease of IdP configuration) fb7cbc42
|
||||
* unlist: now applies to the navpane too fbf17be2
|
||||
* windows: show disk-usage as well, not just disk-free 5c6341e9
|
||||
* #228 nix-pkg improvements (thx @dtomvan!) 4915b14b
|
||||
* docker-compose: ensure logs appear in realtime 3cde1f3b
|
||||
* mention that IdP-volumes and users [can now be persisted](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#but-you-can-enable-idp-volume-persistence) 6069bc9b
|
||||
* #316 explain a scary-looking thing in the code 053de619
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0728-2320 `v1.18.6` reflink-dedup
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #201 add support for reflink-based dedup on cow filesystems df9feabc
|
||||
* combine `--dedup` with `--reflink` to enable, or volflags with same name
|
||||
* a better and safer alternative to the other dedup approaches (symlink/hardlink), but only possible to use in some cases:
|
||||
* needs linux 5.3 or newer, python 3.14 or newer, btrfs/xfs/zfs
|
||||
* not available in the docker images yet; needs a new version of python, so maybe next alpine release (november/december 2025)
|
||||
* ratelimit password changes to impede bruteforcing a2601fd6
|
||||
* limit is set by `--ban-pwc` (default is 5 changes in 60min)
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #240 nixos: fix unixgroups issue (thx @chinponya!) 7c9c962b
|
||||
* #246 cbz: use correct page for thumbnail (thx @Scotsguy!) 542a1de1
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* volflag `nosub` now also prevents mkdir 0f2c6235
|
||||
* improve documentation:
|
||||
* #229 use the same example UDS path everywhere cb019afe
|
||||
* [example nginx config](https://github.com/9001/copyparty/blob/hovudstraum/contrib/nginx/copyparty.conf) had misleading cloudflare comment (thx @jmi2k!) 674fc1fe
|
||||
* more readable `--help-chmod` 03d23dae
|
||||
* #244 fix typo in `--help` 4f013f64
|
||||
* #242 hide "use real pw" on connectpage if no accounts (thx @toast003!) 025942a7
|
||||
* #211 docker: remove deprecated attribute (thx @ptweezy!) 5b98e104
|
||||
* #190 add the feature-showcase video to the readme (thx @RustoMCSpit!) 43e6da34
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0727-2305 `v1.18.5` SECURITY: fix XSS in media tags
|
||||
|
||||
|
|
|
@ -415,6 +415,8 @@ to get started, first `cd` into the `scripts` folder
|
|||
|
||||
* if you want to build the `.pyz` standalone "binary", now run `./make-pyz.sh`
|
||||
|
||||
* if you want to build the `tar.gz` for use in a linux-distro package, now run `./make-tgz-release.sh theVersionNumber`
|
||||
|
||||
* if you want to build a pypi package, now run `./make-pypi-release.sh d`
|
||||
|
||||
* if you want to build a docker-image, you have two options:
|
||||
|
|
16
docs/idp.md
16
docs/idp.md
|
@ -9,9 +9,9 @@ in the copyparty `[global]` config, specify which headers to read client info fr
|
|||
|
||||
# important notes
|
||||
|
||||
## IdP volumes are forgotten on shutdown
|
||||
## by default, IdP volumes are forgotten on shutdown
|
||||
|
||||
IdP volumes, meaning dynamically-created volumes, meaning volumes that contain `${u}` or `${g}` in their URL, will be forgotten during a server restart and then "revived" when the volume's owner sends their first request after the restart
|
||||
IdP volumes, meaning dynamically-created volumes, meaning volumes that contain `${u}` or `${g}` in their URL, will (by default) be forgotten during a server restart and then "revived" when the volume's owner sends their first request after the restart
|
||||
|
||||
until each IdP volume is revived, it will inherit the permissions of its parent volume (if any)
|
||||
|
||||
|
@ -19,7 +19,17 @@ this means that, if an IdP volume is located inside a folder that is readable by
|
|||
|
||||
and likewise -- if the IdP volume is inside a folder that is only accessible by certain users, but the IdP volume is configured to allow access from unauthenticated users, then the contents of the volume will NOT be accessible until it is revived
|
||||
|
||||
until this limitation is fixed (if ever), it is recommended to place IdP volumes inside an appropriate parent volume, so they can inherit acceptable permissions until their revival; see the "strategic volumes" at the bottom of [./examples/docker/idp/copyparty.conf](./examples/docker/idp/copyparty.conf)
|
||||
it is recommended to place IdP volumes inside an appropriate parent volume, so they can inherit acceptable permissions until their revival; see the "strategic volumes" at the bottom of [./examples/docker/idp/copyparty.conf](./examples/docker/idp/copyparty.conf)
|
||||
|
||||
## but you can enable IdP volume persistence
|
||||
|
||||
global-option `idp-store` can enable user/group persistence across restarts;
|
||||
|
||||
* `idp-store: 1` (default) will log users into a database, but not actually "remember" them (the knowledge is ignored)
|
||||
* `idp-store: 2` remembers usernames only
|
||||
* `idp-store: 3` remembers usernames and their groups
|
||||
|
||||
the reason why this is default-disabled, is because you may expect copyparty to forget about a user when you delete them from the IdP-server; this will not be the case any longer, you will need to click `view idp cache` in the controlpanel and manually remove the users you want gone
|
||||
|
||||
|
||||
## Connecting webdav clients
|
||||
|
|
|
@ -22,10 +22,31 @@ update_arch_pkgbuild() {
|
|||
rm -rf x
|
||||
}
|
||||
|
||||
update_mpr_pkgbuild() {
|
||||
cd "$self/../contrib/package/makedeb-mpr"
|
||||
rm -rf x
|
||||
mkdir x
|
||||
|
||||
sha=$(sha256sum "$self/../dist/copyparty-$ver.tar.gz" | awk '{print$1}')
|
||||
|
||||
# awk -v ver=$ver -v sha=$sha '
|
||||
# /^pkgver=/{sub(/[0-9\.]+/,ver)};
|
||||
# /^sha256sums=/{sub(/[0-9a-f]{64}/,sha)};
|
||||
# 1' PKGBUILD >a
|
||||
# mv a PKGBUILD
|
||||
|
||||
echo thing 1
|
||||
sed -s -i "s/pkgver=\"\"/pkgver=\"$ver\"/" PKGBUILD
|
||||
sed -s -i "s/sha256sums=(\".*\")/sha256sums=(\"$sha\")/" PKGBUILD
|
||||
|
||||
rm -rf x
|
||||
}
|
||||
|
||||
update_nixos_pin() {
|
||||
( cd $self/../contrib/package/nix/copyparty;
|
||||
./update.py $self/../dist/copyparty-sfx.py )
|
||||
}
|
||||
|
||||
update_arch_pkgbuild
|
||||
update_mpr_pkgbuild
|
||||
update_nixos_pin
|
||||
|
|
|
@ -14,7 +14,6 @@ clean=--clean
|
|||
|
||||
uname -s | grep WOW64 && m=64 || m=32
|
||||
uname -s | grep NT-10 && w10=1 || w7=1
|
||||
[ $w7 ] && export PRTY_NO_MAGIC=1
|
||||
[ $w7 ] && [ -e up2k.sh ] && [ ! "$1" ] && ./up2k.sh
|
||||
|
||||
[ $w7 ] && pyv=37 || pyv=313
|
||||
|
|
|
@ -143,7 +143,7 @@ class Cfg(Namespace):
|
|||
def __init__(self, a=None, v=None, c=None, **ka0):
|
||||
ka = {}
|
||||
|
||||
ex = "chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ex = "chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ka.update(**{k: False for k in ex.split()})
|
||||
|
||||
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"
|
||||
|
@ -152,6 +152,9 @@ class Cfg(Namespace):
|
|||
ex = "ah_cli ah_gen css_browser dbpath hist ipu js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua ua_nodoc ua_nozip"
|
||||
ka.update(**{k: None for k in ex.split()})
|
||||
|
||||
ex = "gid uid"
|
||||
ka.update(**{k: -1 for k in ex.split()})
|
||||
|
||||
ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz"
|
||||
ka.update(**{k: 1 for k in ex.split()})
|
||||
|
||||
|
|
Loading…
Reference in a new issue