Compare commits

...

40 commits

Author SHA1 Message Date
ed dbd8f837e8 hooks: add wget-i.py (import-safe) (#904) 2025-10-08 22:55:19 +00:00
ed 20ac117c32 update pkgs to 1.19.16 2025-10-05 23:08:25 +00:00
ed cd3feaac86 v1.19.16 2025-10-05 22:59:38 +00:00
ed f8e19815e1 mention ?v suffix for media links (#895) 2025-10-05 21:58:27 +00:00
ed 76e9f23a6d batch-rename: initial counter values 2025-10-05 21:51:41 +00:00
ed 4542ad3c01 hook-flag to send input on stdin 2025-10-05 20:35:03 +00:00
ed 50276c0cfa show mediatags in shares 2025-10-05 20:06:17 +00:00
ed c5f1229685 hide new-share btn in shares 2025-10-05 18:36:10 +00:00
ed 73ec2d296f rss: option to not embed pw in feed 2025-10-05 18:09:05 +00:00
ed a499648291 recommend libvips for heic/heif 2025-10-05 17:24:55 +00:00
ed 4bb5baf1b8 dangit 2025-10-05 00:44:07 +00:00
ed efd19af7ca new hook: granular ramdisk detection 2025-10-05 00:13:34 +00:00
ed aace711eb9 unvendorable surrogateescape (#887);
stolen/surrogateescape.py can be deleted;
this file is only necessary for python2 compatibility
2025-10-04 22:10:34 +00:00
ed 39bd4e5b58 unvendorable dnslib (#887);
stolen/dnslib/ can be deleted and replaced with system lib

NOTE: unvendoring dnslib will make it impossible to communicate with
  devices which have a particular avahi bug; see 6c1cf68bca
2025-10-04 22:09:40 +00:00
ed 08ebb0b4c9 unvendorable qrcodegen (#887);
move copyparty-original code to qrkode.py

stolen/qrcodegen.py can be deleted and replaced with system lib

this is safe and has minimal affect on functionality;
performance will be a tiny bit slower without the vendored copy
2025-10-04 21:36:41 +00:00
ed 656f0a6c39 unvendorable ifaddr (#887);
stolen/ifaddr/ can be deleted and replaced with system lib;
this is safe and will not affect any functionality
2025-10-04 21:33:01 +00:00
ed 805a7054e9 add missing licenses (#887);
* added missing license entry for fusepy
* added missing license entry for DOMPurify
* aligned license names with SPDX identifiers
2025-10-04 21:29:08 +00:00
ed 01709691f2 git-mv ofl.r13 2025-10-04 21:22:28 +00:00
ed 41ed559faa hooks: import-flag 2025-10-04 13:32:26 +00:00
ed a0f8f794a8 mention termux fix 2025-10-04 12:03:37 +00:00
ed fbe5fa582e helptext fix 2025-10-04 12:00:42 +00:00
ed 2248705e1a --ui-filesz can have trailing dash 2025-10-04 11:59:53 +00:00
ed eb173be4f1 folder-thumbs: cbz, epub 2025-10-04 11:57:43 +00:00
ed d05a88d2ee add rename counters; closes #854 2025-10-04 11:53:59 +00:00
ed 09e6f29e5e indent 2025-10-04 11:42:43 +00:00
ed 2ce32e4fb6 apply vol-favicon on nav; closes #882 2025-10-04 11:16:16 +00:00
ed 9b7f933b78 optimize --name-url (#884) 2025-10-04 10:45:17 +00:00
Lulu 38cc809822
Add --name-url option (#884)
Turns the server name into a hyperlink to a spefified URL

Can link back to homepage with `--name-url=/`, controlpanel with
`name-url="/?h"`, or external sites with `name-url="https://foo.bar/"`
2025-10-04 10:10:48 +00:00
ed e9b6e645d3 fix buildscript perm 2025-10-04 09:41:05 +00:00
ed 0f9a239078 allow favicon.png/gif (samsung-android) 2025-10-04 09:39:13 +00:00
ed 0453b7ac53 xhrchk: generic error only as fallback 2025-10-04 09:38:34 +00:00
Chloe Surett 1bcdf8c9e3
Add Blu-ray discs to fuzzy file size type (#878)
Signed-off-by: Chloe Surett <chloe@surett.me>
Signed-off-by: ed <s@ocv.me>
Co-authored-by: ed <s@ocv.me>
2025-10-04 09:35:58 +00:00
ed 4177c1d9ed epub: handle missing covers; closes #860 2025-10-04 09:30:43 +00:00
ed 171ca985c8 bbox: flex conditionally 2025-10-04 09:23:42 +00:00
AppleTheGolden dacc64dd2e
baguettebox: RTL support (#881) 2025-10-04 09:12:02 +00:00
Kaleb Debre 31f1b535b2
nixos: unix-user/group to run as (#886)
Co-authored-by: Kaleb Debre <kaleb.debre@web.de>
2025-10-03 17:13:28 +02:00
Daniel Lovegrove 7fc379abc8
Add setup example for running with Podman under systemd (#460)
* Create copyparty.container file
* Add and document rootful configuration
* Add non-root config, clean up README

Signed-off-by: Daniel Lovegrove <d.lovegrove11@gmail.com>
2025-10-03 05:45:44 +02:00
ed a8f53d5ef0 shrink docker-min from 45 to 33 MiB 2025-09-30 23:13:46 +00:00
ed 3f59710294 allow chpw with idp; closes #872 2025-09-30 21:26:37 +00:00
ed 24e01221c5 update pkgs to 1.19.15 2025-09-29 23:19:00 +00:00
41 changed files with 1245 additions and 216 deletions

View file

@ -138,6 +138,7 @@ made in Norway 🇳🇴
* [dependencies](#dependencies) - mandatory deps
* [optional dependencies](#optional-dependencies) - install these to enable bonus features
* [dependency chickenbits](#dependency-chickenbits) - prevent loading an optional dependency
* [dependency unvendoring](#dependency-unvendoring) - force use of system modules
* [optional gpl stuff](#optional-gpl-stuff)
* [sfx](#sfx) - the self-contained "binary" (recommended!)
* [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
@ -213,6 +214,7 @@ you may also want these, especially on servers:
* [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service (see guide inside)
* [contrib/systemd/prisonparty.service](contrib/systemd/prisonparty.service) to run it in a chroot (for extra security)
* [contrib/podman-systemd/](contrib/podman-systemd/) to run copyparty in a Podman container as a systemd service (see guide inside)
* [contrib/openrc/copyparty](contrib/openrc/copyparty) to run copyparty on Alpine / Gentoo
* [contrib/rc/copyparty](contrib/rc/copyparty) to run copyparty on FreeBSD
* [nixos module](#nixos-module) to run copyparty on NixOS hosts
@ -729,6 +731,10 @@ to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` an
* the supported image formats are [jpg, png, gif, webp, ico](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types)
* be careful with svg; chrome will crash if you have too many unique svg files showing on the same page (the limit is 250 or so) -- showing the same handful of svg files thousands of times is ok however
note:
* heif/heifs/heic/heics images usually require the `libvips` [optional dependency](#optional-dependencies) (available in the `iv` docker image, `withFastThumbnails` in nixos)
* technical trivia: FFmpeg has basic support for tiled heic as of v7.0; need `-show_stream_groups` for correct resolution
config file example:
```yaml
@ -997,6 +1003,8 @@ available functions:
* `$lpad(text, length, pad_char)`
* `$rpad(text, length, pad_char)`
two counters are available; `.n.s` is the nth file in the selection, and `.n.d` the nth file in the folder, for example rename-output `file(.n.d).(ext)` gives `file5.bin`, and `beach-$lpad((.n.s),3,0).(ext)` is `beach-017.jpg` and the initial value of each counter can be set in the textboxes underneath the preset dropdown
so,
say you have a file named [`meganeko - Eclipse - 07 Sirius A.mp3`](https://www.youtube.com/watch?v=-dtb0vDPruI) (absolutely fantastic album btw) and the tags are: `Album:Eclipse`, `Artist:meganeko`, `Title:Sirius A`, `tn:7`
@ -1032,6 +1040,8 @@ url parameters:
* `pw=hunter2` for password auth
* if you enabled `--usernames` then do `pw=username:password` instead
* `nopw` disables embedding the password (if provided) into item-URLs in the feed
* `nopw=a` disables mentioning the password anywhere at all in the feed; may break some readers
* `recursive` to also include subfolders
* `title=foo` changes the feed title (default: folder name)
* `fext=mp3,opus` only include mp3 and opus files (default: all)
@ -1098,6 +1108,7 @@ some highlights:
* shows the audio waveform in the seekbar
* not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
* videos can be played as audio, without wasting bandwidth on the video
* adding `?v` to the end of an audio/video/image link will make it open in the mediaplayer
click the `play` link next to an audio file, or copy the link target to [share it](https://a.ocv.me/pub/demo/music/Ubiktune%20-%20SOUNDSHOCK%202%20-%20FM%20FUNK%20TERRROR!!/#af-1fbfba61&t=18) (optionally with a timestamp to start playing from, like that example does)
@ -2465,6 +2476,10 @@ copyparty on NixOS is configured via `services.copyparty` options, for example:
```nix
services.copyparty = {
enable = true;
# the user to run the service as
user = "copyparty";
# the group to run the service as
group = "copyparty";
# directly maps to values in the [global] section of the copyparty config.
# see `copyparty --help` for available options
settings = {
@ -2489,6 +2504,12 @@ services.copyparty = {
k.passwordFile = "/run/keys/copyparty/k_password";
};
# create a group
groups = {
# users "ed" and "k" are part of the group g1
g1 = [ "ed" "k" ];
};
# create a volume
volumes = {
# create a volume at "/" (the webroot), which will
@ -2978,6 +2999,20 @@ example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
* python2.7 on windows: `PRTY_NO_FFMPEG` + `PRTY_NO_FFPROBE` saves startup time
### dependency unvendoring
force use of system modules instead of the vendored versions:
| env-var | what it does |
| -------------------- | ------------ |
| `PRTY_SYS_ALL` | all of the below |
| `PRTY_SYS_DNSLIB` | replace [stolen/dnslib](./copyparty/stolen/dnslib) with [upstream](https://pypi.org/project/dnslib/) |
| `PRTY_SYS_IFADDR` | replace [stolen/ifaddr](./copyparty/stolen/ifaddr) with [upstream](https://pypi.org/project/ifaddr/) |
| `PRTY_SYS_QRCG` | replace [stolen/qrcodegen.py](./copyparty/stolen/qrcodegen.py) with [upstream](https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py) |
to debug, run copyparty with `PRTY_MODSPEC=1` to see where it's getting each module from
## optional gpl stuff
some bundled tools have copyleft dependencies, see [./bin/#mtag](bin/#mtag)
@ -3040,6 +3075,8 @@ if you want thumbnails (photos+videos) and you're okay with spending another 132
* or if you want to use `vips` for photo-thumbs instead, `pkg install libvips && python -m pip install --user -U wheel && python -m pip install --user -U pyvips && (cd /data/data/com.termux/files/usr/lib/; ln -s libgobject-2.0.so{,.0}; ln -s libvips.so{,.42})`
if you are suddenly unable to access storage (permission issues), try forcequitting termux, revoke all of its permissions in android settings, and run the command `termux-setup-storage`
# install on iOS

View file

@ -28,10 +28,19 @@ these are `--xiu` hooks; unlike `xbu` and `xau` (which get executed on every sin
* [reject-extension.py](reject-extension.py) rejects uploads if they match a list of file extensions
* [reloc-by-ext.py](reloc-by-ext.py) redirects an upload to another destination based on the file extension
* good example of the `reloc` [hook effect](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#hook-effects)
* [reject-and-explain.py](reject-and-explain.py) shows a custom error-message when it rejects an upload
* [reject-ramdisk.py](reject-ramdisk.py) rejects the upload if the destination is a ramdisk
* this hook uses the `I` flag which makes it 140x faster, but if the plugin has a bug it may crash copyparty
# on message
* [wget.py](wget.py) lets you download files by POSTing URLs to copyparty
* [wget-i.py](wget-i.py) is an import-safe modification of this hook (starts 140x faster, but higher chance of bugs)
* [qbittorrent-magnet.py](qbittorrent-magnet.py) starts downloading a torrent if you post a magnet url
* [usb-eject.py](usb-eject.py) adds web-UI buttons to safe-remove usb flashdrives shared through copyparty
* [msg-log.py](msg-log.py) is a guestbook; logs messages to a doc in the same folder
# general concept demos
* [import-me.py](import-me.py) shows how the `I` flag makes the hook 140x faster (but you need to be Very Careful when writing the plugin)
* [wget-i.py](wget-i.py) is an import-safe modification of [wget.py](wget.py)

55
bin/hooks/import-me.py Normal file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env python3
from typing import Any
_ = r"""
the fastest hook in the west
(runs directly inside copyparty, not as a subprocess)
example usage as global config:
--xbu I,bin/hooks/import-me.py
example usage as a volflag (per-volume config):
-v srv/inc:inc:r:rw,ed:c,xbu=I,bin/hooks/import-me.py
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(share filesystem-path srv/inc as volume /inc,
readable by everyone, read-write for user 'ed',
running this plugin on all uploads with the params listed below)
example usage as a volflag in a copyparty config file:
[/inc]
srv/inc
accs:
r: *
rw: ed
flags:
xbu: I,bin/hooks/import-me.py
parameters explained,
I = import; do not fork / subprocess
IMPORTANT NOTE:
because this hook is running inside copyparty, you need to
be EXCEPTIONALLY CAREFUL to avoid side-effects, for example
DO NOT os.chdir() or anything like that, and also make sure
that the name of this file is unique (cannot be the same as
an existing python module/library)
"""
def main(ka: dict[str, Any]) -> dict[str, Any]:
# "ka" is a dictionary with info from copyparty...
# but because we are running inside copyparty, we don't need such courtesies;
import inspect
cf = inspect.currentframe().f_back.f_back.f_back
t = "hello from hook; I am able to peek into copyparty's memory like so:\n function name: %s\n variables:\n %s\n"
t2 = "\n ".join([("%r: %r" % (k, v))[:99] for k, v in cf.f_locals.items()][:9])
logger = ka["log"]
logger(t % (cf.f_code, t2))
# must return a dictionary with:
# "rc": the retcode; 0 is ok
return {"rc": 0}

View file

@ -0,0 +1,72 @@
#!/usr/bin/env python3
import os
import threading
from argparse import Namespace
from jinja2.nodes import Name
from copyparty.fsutil import Fstab
from typing import Any, Optional
_ = r"""
reject an upload if the target folder is on a ramdisk; useful when you
have a volume where some folders inside are ramdisks but others aren't
example usage as global config:
--xbu I,bin/hooks/reject-ramdisk.py
example usage as a volflag (per-volume config):
-v srv/inc:inc:r:rw,ed:c,xbu=I,bin/hooks/reject-ramdisk.py
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(share filesystem-path srv/inc as volume /inc,
readable by everyone, read-write for user 'ed',
running this plugin on all uploads with the params listed below)
example usage as a volflag in a copyparty config file:
[/inc]
srv/inc
accs:
r: *
rw: ed
flags:
xbu: I,bin/hooks/reject-ramdisk.py
parameters explained,
I = import; do not fork / subprocess
IMPORTANT NOTE:
because this hook is imported inside copyparty, you need to
be EXCEPTIONALLY CAREFUL to avoid side-effects, for example
DO NOT os.chdir() or anything like that, and also make sure
that the name of this file is unique (cannot be the same as
an existing python module/library)
"""
mutex = threading.Lock()
fstab: Optional[Fstab] = None
def main(ka: dict[str, Any]) -> dict[str, Any]:
global fstab
with mutex:
log = ka["log"] # this is a copyparty NamedLogger function
if not fstab:
log("<HOOK:RAMDISK> creating fstab", 6)
args = Namespace()
args.mtab_age = 1 # cache the filesystem info for 1 sec
fstab = Fstab(log, args, False)
ap = ka["ap"] # abspath the upload is going to
fs, mp = fstab.get(ap) # figure out what the filesystem is
ramdisk = fs in ("tmpfs", "overlay") # looks like a ramdisk?
# log("<HOOK:RAMDISK> fs=%r" % (fs,))
if ramdisk:
t = "Upload REJECTED because destination is a ramdisk"
return {"rc": 1, "rejectmsg": t}
return {"rc": 0}

97
bin/hooks/wget-i.py Executable file
View file

@ -0,0 +1,97 @@
#!/usr/bin/env python3
import os
import threading
import subprocess as sp
_ = r"""
use copyparty as a file downloader by POSTing URLs as
application/x-www-form-urlencoded (for example using the
📟 message-to-server-log in the web-ui)
this hook is a modified copy of wget.py, modified to
make it import-safe so it can be run with the 'I' flag,
which speeds up the startup time of the hook by 140x
example usage as global config:
--xm aw,I,bin/hooks/wget-i.py
parameters explained,
xm = execute on message-to-server-log
aw = only users with write-access can use this
I = import; do not fork / subprocess
example usage as a volflag (per-volume config):
-v srv/inc:inc:r:rw,ed:c,xm=aw,I,bin/hooks/wget.py
^^^^^^^^^^^^^^^^^^^^^^^^^^^
(share filesystem-path srv/inc as volume /inc,
readable by everyone, read-write for user 'ed',
running this plugin on all messages with the params explained above)
example usage as a volflag in a copyparty config file:
[/inc]
srv/inc
accs:
r: *
rw: ed
flags:
xm: aw,I,bin/hooks/wget.py
the volflag examples only kicks in if you send the message
while you're in the /inc folder (or any folder below there)
IMPORTANT NOTE:
because this hook uses the 'I' flag to run inside copyparty,
many other flags will not work (f,j,c3,t3600 as seen in the
original wget.py), and furthermore + more importantly we
need to be EXCEPTIONALLY CAREFUL to avoid side-effects, so
the os.chdir has been replaced with cwd=dirpath for example
"""
def do_stuff(inf):
"""
worker function which is executed in another thread to
avoid blocking copyparty while the download is running,
since we cannot use the 'f,t3600' hook-flags with 'I'
"""
# first things first; grab the logger-function which copyparty is letting us borrow
log = inf["log"]
url = inf["txt"]
if "://" not in url:
url = "https://" + url
proto = url.split("://")[0].lower()
if proto not in ("http", "https", "ftp", "ftps"):
raise Exception("bad proto {}".format(proto))
dirpath = inf["ap"]
name = url.split("?")[0].split("/")[-1]
msg = "-- DOWNLOADING " + name
log(msg)
tfn = os.path.join(dirpath, msg)
open(tfn, "wb").close()
cmd = ["wget", "--trust-server-names", "-nv", "--", url]
try:
# two things to note here:
# - cannot use the `c3` hook-flag with `I` so mute output with stdout=sp.DEVNULL instead;
# - MUST NOT use os.chdir with 'I' so use cwd=dirpath instead
sp.check_call(cmd, cwd=dirpath, stdout=sp.DEVNULL)
except:
t = "-- FAILED TO DOWNLOAD " + name
log(t, 3) # 3=yellow=warning
open(os.path.join(dirpath, t), "wb").close()
raise # have copyparty scream about the details in the log
os.unlink(tfn)
def main(inf):
threading.Thread(target=do_stuff, args=(inf,), daemon=True).start()

View file

@ -370,12 +370,16 @@ in
) cfg.volumes
);
users.groups.copyparty = lib.mkIf (cfg.user == "copyparty" && cfg.group == "copyparty") { };
users.users.copyparty = lib.mkIf (cfg.user == "copyparty" && cfg.group == "copyparty") {
description = "Service user for copyparty";
group = "copyparty";
home = externalStateDir;
isSystemUser = true;
users.groups = lib.mkIf (cfg.group == "copyparty") {
copyparty = { };
};
users.users = lib.mkIf (cfg.user == "copyparty") {
copyparty = {
description = "Service user for copyparty";
group = cfg.group;
home = externalStateDir;
isSystemUser = true;
};
};
environment.systemPackages = lib.mkIf cfg.mkHashWrapper [
(pkgs.writeShellScriptBin "copyparty-hash" ''
@ -394,4 +398,3 @@ in
}
);
}

View file

@ -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.19.14"
pkgver="1.19.16"
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}/copyparty.conf" )
sha256sums=("fd4672984779c02afd864df8c70974575acff5a3d8378ff4f15fc9bdb497cb1b")
sha256sums=("d8cc10d3623eaccc8acaebdd3b0102a1ebf878a03ffe6e737d132c66f799d682")
build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -2,7 +2,7 @@
pkgname=copyparty
pkgver=1.19.14
pkgver=1.19.16
pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
arch=("any")
@ -20,7 +20,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=("fd4672984779c02afd864df8c70974575acff5a3d8378ff4f15fc9bdb497cb1b")
sha256sums=("d8cc10d3623eaccc8acaebdd3b0102a1ebf878a03ffe6e737d132c66f799d682")
build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -1,5 +1,5 @@
{
"url": "https://github.com/9001/copyparty/releases/download/v1.19.14/copyparty-1.19.14.tar.gz",
"version": "1.19.14",
"hash": "sha256-/UZymEd5wCr9hk34xwl0V1rP9aPYN4/08V/JvbSXyxs="
"url": "https://github.com/9001/copyparty/releases/download/v1.19.16/copyparty-1.19.16.tar.gz",
"version": "1.19.16",
"hash": "sha256-2MwQ02I+rMyKyuvdOwECoev4eKA//m5zfRMsZveZ1oI="
}

View file

@ -0,0 +1,173 @@
# copyparty with Podman and Systemd
Use this configuration if you want to run copyparty in a Podman container, with the reliability of running the container under a systemd service.
Documentation for `.container` files can be found in the [Container unit](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html#container-units-container) docs. Systemd does not understand `.container` files natively, so Podman converts these to `.service` files with a [systemd-generator](https://www.freedesktop.org/software/systemd/man/latest/systemd.generator.html). This process is transparent, but sometimes needs to be debugged in case your `.container` file is malformed. There are instructions to debug the systemd generator in the Troubleshooting section below.
To run copyparty in this way, you must already have podman installed. To install Podman, see: https://podman.io/docs/installation
There is a sample configuration file in the same directory as this file (`copyparty.conf`).
## Run the container as root
Running the container as the root user is easy to set up, but less secure. There are instructions in the next section to run the container as a rootless user if you'd rather run the container like that.
First, change this line in the `copyparty.container` file to reflect the directory you want to share. By default, it shares `/mnt/` but you'll probably want to change that.
```
# Change /mnt to something you want to share
Volume=/mnt:/w:z
```
Note that you can select the owner and group of this volume by changing the `uid:` and `gid:` of the volume in `copyparty.conf`, but for simplicity let's assume you want it to be owned by `root:root`.
To install and start copyparty with Podman and systemd as the root user, run the following:
```shell
sudo mkdir -pv /etc/systemd/container/ /etc/copyparty/
sudo cp -v copyparty.container /etc/systemd/containers/
sudo cp -v copyparty.conf /etc/copyparty/
sudo systemctl daemon-reload
sudo systemctl start copyparty
```
Note: You can't "enable" this kind of Podman service. The `[Install]` section of the `.container` file effectively handles enabling the service so that it starts when the server reboots.
You can see the status of the service with:
```shell
sudo systemctl status -a copyparty
```
You can see (and follow) the logs with either of these commands:
```shell
sudo podman logs -f copyparty
# -a is required or else you'll get output like: copyparty[549025]: [649B blob data]
sudo journalctl -a -f -u copyparty
```
## Run the container as a non-root user
This configuration is more secure, but is more involved and requires ensuring files have proper permissions. You will need a root user account to do some of this setup.
First, you need a user to run the container as. In this example we'll create a "podman" user with UID=1001 and GID=1001.
```shell
sudo groupadd -g 1001 podman
sudo useradd -u 1001 -m podman
sudo usermod -aG podman podman
sudo loginctl enable-linger podman
# Set a strong password for this user
sudo -u podman passwd
```
The `enable-linger` command allows the podman user to run systemd user services that persist even when the user is not logged in. You could use a user that already exists in the system to run this service as, just make sure to run `loginctl enable-linger USERNAME` for that user.
Next, change these lines in the `copyparty.container` file to reflect the config directory and the directory you want to share. By default, the config shares `/home/podman/copyparty/sharing/` but you'll probably want to change this:
```
# Change to reflect your non-root user's home directory
Volume=/home/podman/copyparty/config:/cfg:z
# Change to the directory you want to share
Volume=/home/podman/copyparty/sharing:/w:z
```
Make sure the podman user has read/write access to both of these directories.
Next, **log in to the server as the podman user**.
To install and start copyparty as the non-root podman user, run the following:
```shell
mkdir -pv /home/podman/.config/containers/systemd/ /home/podman/copyparty/config
cp -v copyparty.container /home/podman/.config/containers/systemd/copyparty.container
cp -v copyparty.conf /home/podman/copyparty/config
systemctl --user daemon-reload
systemctl --user start copyparty
```
**Important note: Never use `sudo` with `systemctl --user`!**
You can check the status of the user service with:
```shell
systemctl --user status -a copyparty
```
You can see (and follow) the logs with:
```shell
podman logs -f copyparty
journalctl --user -a -f -u copyparty
```
## Troubleshooting
If the container fails to start, and you've modified the `.container` service, it's likely that your `.container` file failed to be translated into a `.service` file. You can debug the podman service generator with this command:
```shell
sudo /usr/lib/systemd/system-generators/podman-system-generator --dryrun
```
## Allowing Traffic from Outside your Server
To allow traffic on port 3923 of your server, you should run:
```shell
sudo firewall-cmd --permanent --add-port=3923/tcp
sudo firewall-cmd --reload
```
Otherwise, you won't be able to access the copyparty server from anywhere other than the server itself.
## Updating copyparty
To update the version of copyparty used in the container, you can:
```shell
# If root:
sudo podman pull docker.io/copyparty/ac:latest
sudo systemctl restart copyparty
# If non-root:
podman pull docker.io/copyparty/ac:latest
systemctl --user restart copyparty
```
Or, you can change the pinned version of the image in the `[Container]` section of the `.container` file and run:
```shell
# If root:
sudo systemctl daemon-reload
sudo systemctl restart copyparty
# If non-root:
systemctl --user daemon-reload
systemctl --user restart copyparty
```
Podman will pull the image you've specified when restarting. If you have it set to `:latest`, Podman does not know to re-pull the container.
### Enabling auto-update
Alternatively, you can enable auto-updates by un-commenting this line:
```
# AutoUpdate=registry
```
You will also need to enable the [podman auto-updater service](https://docs.podman.io/en/latest/markdown/podman-auto-update.1.html) with:
```shell
# If root:
sudo systemctl enable podman-auto-update.timer podman-auto-update.service
# If non-root:
systemctl --user enable podman-auto-update.timer podman-auto-update.service
```
This works best if you always want the latest version of copyparty. The auto-updater runs once every 24 hours.

View file

@ -0,0 +1,36 @@
[global]
e2dsa # enable file indexing and filesystem scanning
e2ts # and enable multimedia indexing
ansi # and colors in log messages
# uncomment the line starting with q, lo: to log to a file instead of stdout/journalctl;
# $LOGS_DIRECTORY is usually /var/log/copyparty (comes from systemd)
# and copyparty replaces %Y-%m%d with Year-MonthDay, so the
# full path will be something like /var/log/copyparty/2023-1130.txt
# (note: enable compression by adding .xz at the end)
# q, lo: $LOGS_DIRECTORY/%Y-%m%d.log
# p: 80,443,3923 # listen on 80/443 as well (requires CAP_NET_BIND_SERVICE)
# i: 127.0.0.1 # only allow connections from localhost (reverse-proxies)
# ftp: 3921 # enable ftp server on port 3921
# p: 3939 # listen on another port
# df: 16 # stop accepting uploads if less than 16 GB free disk space
# ver # show copyparty version in the controlpanel
# grid # show thumbnails/grid-view by default
# theme: 2 # monokai
# name: datasaver # change the server-name that's displayed in the browser
# stats, nos-dup # enable the prometheus endpoint, but disable the dupes counter (too slow)
# no-robots, force-js # make it harder for search engines to read your server
[accounts]
ed: wark # username: password
[/] # create a volume at "/" (the webroot), which will
/w # share the contents of the "/w" folder
accs:
rw: * # everyone gets read-write access, but
rwmda: ed # the user "ed" gets read-write-move-delete-admin
# uid: 1000 # If you're running as root, you can change the owner of this volume here
# gid: 1000 # If you're running as root, you can change the group of this volume here

View file

@ -0,0 +1,55 @@
[Container]
# It's recommended to replace :latest with a specific version
# for example: docker.io/copyparty/ac:1.19.15
Image=docker.io/copyparty/ac:latest
ContainerName=copyparty
# Uncomment to enable auto-updates
# AutoUpdate=registry
# Environment variables
# enable mimalloc by replacing "NOPE" with "2" for a nice speed-boost (will use twice as much ram)
Environment=LD_PRELOAD=/usr/lib/libmimalloc-secure.so.NOPE
# ensures log-messages are not delayed (but can reduce speed a tiny bit)
Environment=PYTHONUNBUFFERED=1
# Ports
PublishPort=3923:3923
# Volumes (PLEASE LOOK!)
# Rootful setup:
# Leave as-is
# Non-root setup:
# Change /etc/copyparty to /home/<USER>/copyparty/config
Volume=/etc/copyparty:/cfg:z
# Rootful setup:
# Change /mnt to the directory you want to share
# Non-root setup:
# Change /mnt to something owned by your user, e.g., /home/<USER>/copyparty/sharing:/w:z
Volume=/mnt:/w:z
# Give the container time to stop in case the thumbnailer is still running.
# It's allowed to continue finishing up for 10s after the shutdown signal, give it a 5s buffer
StopTimeout=15
# hide it from logs with "/._" so it matches the default --lf-url filter
HealthCmd="wget --spider -q 127.0.0.1:3923/?reset=/._"
HealthInterval=1m
HealthTimeout=2s
HealthRetries=5
HealthStartPeriod=15s
[Unit]
After=default.target
[Install]
# Start by default on boot
WantedBy=default.target
[Service]
# Give the container time to start in case it needs to pull the image
TimeoutStartSec=600

View file

@ -813,9 +813,11 @@ def get_sects():
\033[36mf\033[35m forks the process, doesn't wait for completion
\033[36mc\033[35m checks return code, blocks the action if non-zero
\033[36mj\033[35m provides json with info as 1st arg instead of filepath
\033[36ms\033[35m provides input data on stdin (instead of 1st arg)
\033[36mwN\033[35m waits N sec after command has been started before continuing
\033[36mtN\033[35m sets an N sec timeout before the command is abandoned
\033[36miN\033[35m xiu only: volume must be idle for N sec (default = 5)
\033[36mI\033[35m import and run as module, not as subprocess
\033[36mar\033[35m only run hook if user has read-access
\033[36marw\033[35m only run hook if user has read-write-access
@ -845,6 +847,9 @@ def get_sects():
the \033[33m--\033[35m stops notify-send from reading the message as args
and the alert will be "hey" followed by the messagetext
\033[36m--xm s,,tee,-a,log.txt\033[35m appends each msg to log.txt;
\033[36m--xm s,j,,tee,-a,log.txt\033[35m writes it as json instead
\033[36m--xau zmq:pub:tcp://*:5556\033[35m announces uploads on zeromq;
\033[36m--xau t3,zmq:push:tcp://*:5557\033[35m also works, and you can
\033[36m--xau t3,j,zmq:req:tcp://localhost:5555\033[35m too for example
@ -854,7 +859,8 @@ def get_sects():
as soon as the volume has been idle for iN seconds (5 by default)
\033[36mxiu\033[0m is also unique in that it will pass the metadata to the
executed program on STDIN instead of as argv arguments, and
executed program on STDIN instead of as argv arguments (so
just like the \033[36ms\033[0m option does for the other hook types), and
it also includes the wark (file-id/hash) as a json property
\033[36mxban\033[0m can be used to overrule / cancel a user ban event;
@ -865,6 +871,12 @@ def get_sects():
on new uploads, but with certain limitations. See
bin/hooks/reloc* and docs/devnotes.md#hook-effects
the \033[36mI\033[0m option will override most other options, because
it entirely hands over control to the hook, which is
then able to tamper with copyparty's internal memory
and wreck havoc if it wants to -- but this is worh it
because it makes the hook 140x faster
except for \033[36mxm\033[0m, only one hook / one action can run at a time,
so it's recommended to use the \033[36mf\033[0m flag unless you really need
to wait for the hook to finish before continuing (without \033[36mf\033[0m
@ -1155,6 +1167,8 @@ def add_general(ap, nc, srvname):
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,xm", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]")
ap2.add_argument("--name", metavar="TXT", type=u, default=srvname, help="server name (displayed topleft in browser and in mDNS)")
ap2.add_argument("--name-url", metavar="TXT", type=u, help="URL for server name hyperlink (displayed topleft in browser)")
ap2.add_argument("--name-html", type=u, help=argparse.SUPPRESS)
ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="\033[34mREPEATABLE:\033[0m map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
@ -1539,7 +1553,7 @@ def add_safety(ap):
ap2.add_argument("--ban-422", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m 422's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes (invalid requests, attempted exploits ++)")
ap2.add_argument("--ban-url", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m sus URL's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; applies only to permissions g/G/h (decent replacement for \033[33m--ban-404\033[0m if that can't be used)")
ap2.add_argument("--sus-urls", metavar="R", type=u, default=r"\.php$|(^|/)wp-(admin|content|includes)/", help="URLs which are considered sus / eligible for banning; disable with blank or [\033[32mno\033[0m]")
ap2.add_argument("--nonsus-urls", metavar="R", type=u, default=r"^(favicon\.ico|robots\.txt)$|^apple-touch-icon|^\.well-known", help="harmless URLs ignored from 404-bans; disable with blank or [\033[32mno\033[0m]")
ap2.add_argument("--nonsus-urls", metavar="R", type=u, default=r"^(favicon\..{3}|robots\.txt)$|^apple-touch-icon|^\.well-known", help="harmless URLs ignored from 403/404-bans; disable with blank or [\033[32mno\033[0m]")
ap2.add_argument("--early-ban", action="store_true", help="if a client is banned, reject its connection as soon as possible; not a good idea to enable when proxied behind cloudflare since it could ban your reverse-proxy")
ap2.add_argument("--cookie-nmax", metavar="N", type=int, default=50, help="reject HTTP-request from client if they send more than N cookies")
ap2.add_argument("--cookie-cmax", metavar="N", type=int, default=8192, help="reject HTTP-request from client if more than N characters in Cookie header")

View file

@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (1, 19, 15)
VERSION = (1, 19, 16)
CODENAME = "usernames"
BUILD_DT = (2025, 9, 29)
BUILD_DT = (2025, 10, 5)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View file

@ -2514,13 +2514,17 @@ class AuthSrv(object):
ico_url = vol.flags.get("ufavico")
if ico_url:
ico_h = ""
ico_ext = ico_url.split("?")[0].split(".")[-1].lower()
if ico_ext in FAVICON_MIMES:
zs = '<link rel="icon" type="%s" href="%s">\n'
head_s += zs % (FAVICON_MIMES[ico_ext], ico_url)
ico_h = zs % (FAVICON_MIMES[ico_ext], ico_url)
elif ico_ext == "ico":
zs = '<link rel="shortcut icon" href="%s">\n'
head_s += zs % (ico_url,)
ico_h = zs % (ico_url,)
if ico_h:
vol.flags["ufavico_h"] = ico_h
head_s += ico_h
if head_s:
vol.flags["html_head_s"] = head_s
@ -2942,6 +2946,11 @@ class AuthSrv(object):
shn.shr_src = (s_vfs, s_rem)
shn.realpath = s_vfs.canonical(s_rem)
o_vn, _ = shn._get_share_src("")
shn.flags = o_vn.flags.copy()
shn.dbpath = o_vn.dbpath
shn.histpath = o_vn.histpath
# root.all_aps doesn't include any shares, so make a copy where the
# share appears in all abspaths it can provide (for example for chk_ap)
ap = shn.realpath
@ -3005,6 +3014,8 @@ class AuthSrv(object):
"unlist": vf.get("unlist") or "",
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
}
if "ufavico_h" in vf:
vn.js_ls["ufavico"] = vf["ufavico_h"]
js_htm = {
"SPINNER": self.args.spinner,
"s_name": self.args.bname,
@ -3040,7 +3051,7 @@ class AuthSrv(object):
"dvol": self.args.au_vol,
"idxh": int(self.args.ih),
"dutc": not self.args.localtime,
"dfszf": self.args.ui_filesz,
"dfszf": self.args.ui_filesz.strip("-"),
"themes": self.args.themes,
"turbolvl": self.args.turbo,
"nosubtle": self.args.nosubtle,

View file

@ -34,8 +34,8 @@ from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode
from .__version__ import S_VERSION
from .authsrv import LEELOO_DALLAS, VFS # typechk
from .bos import bos
from .qrkode import QrCode, qr2svg, qrgen
from .star import StreamTar
from .stolen.qrcodegen import QrCode, qr2svg
from .sutil import StreamArc, gfilter
from .szip import StreamZip
from .up2k import up2k_chunksize
@ -1425,10 +1425,10 @@ class HttpCli(object):
hits = idx.run_query(self.uname, [self.vn], uq, uv, False, False, nmax)[0]
pw = self.ouparam.get("pw")
if pw:
q_pw = "?pw=%s" % (html_escape(pw, True, True),)
a_pw = "&pw=%s" % (html_escape(pw, True, True),)
if "pw" in self.ouparam and "nopw" not in self.ouparam:
zs = self.ouparam["pw"]
q_pw = "?pw=%s" % (quotep(zs),)
a_pw = "&pw=%s" % (quotep(zs),)
for i in hits:
i["rp"] += a_pw if "?" in i["rp"] else q_pw
else:
@ -1442,6 +1442,8 @@ class HttpCli(object):
self.host,
)
feed = baseurl + self.req[1:]
if "pw" in self.ouparam and self.ouparam.get("nopw") == "a":
feed = re.sub(r"&pw=[^&]*", "", feed)
if self.is_vproxied:
baseurl += self.args.RS
efeed = html_escape(feed, True, True)
@ -4951,7 +4953,7 @@ class HttpCli(object):
url += "#" + uhash
self.log("qrcode(%r)" % (url,))
ret = qr2svg(QrCode.encode_binary(url.encode("utf-8")), 2)
ret = qr2svg(qrgen(url.encode("utf-8")), 2)
self.reply(ret.encode("utf-8"), mime="image/svg+xml")
return True
@ -6493,7 +6495,7 @@ class HttpCli(object):
try:
if not self.args.nih:
srv_info.append(self.args.name)
srv_info.append(self.args.name_html)
except:
self.log("#wow #whoa")

View file

@ -2,6 +2,7 @@
from __future__ import print_function, unicode_literals
import errno
import os
import random
import select
import socket
@ -12,31 +13,64 @@ from ipaddress import IPv4Network, IPv6Network
from .__init__ import TYPE_CHECKING
from .__init__ import unicode as U
from .multicast import MC_Sck, MCast
from .stolen.dnslib import (
AAAA,
)
from .stolen.dnslib import CLASS as DC
from .stolen.dnslib import (
NSEC,
PTR,
QTYPE,
RR,
SRV,
TXT,
A,
DNSHeader,
DNSQuestion,
DNSRecord,
set_avahi_379,
)
from .util import IP6_LL, CachedSet, Daemon, Netdev, list_ips, min_ex
try:
if os.getenv("PRTY_SYS_ALL") or os.getenv("PRTY_SYS_DNSLIB"):
raise ImportError()
from .stolen.dnslib import (
AAAA,
)
from .stolen.dnslib import CLASS as DC
from .stolen.dnslib import (
NSEC,
PTR,
QTYPE,
RR,
SRV,
TXT,
A,
DNSHeader,
DNSQuestion,
DNSRecord,
set_avahi_379,
)
DNS_VND = True
except ImportError:
DNS_VND = False
from dnslib import (
AAAA,
)
from dnslib import CLASS as DC
from dnslib import (
NSEC,
PTR,
QTYPE,
RR,
SRV,
TXT,
A,
Bimap,
DNSHeader,
DNSQuestion,
DNSRecord,
)
DC.forward[0x8001] = "F_IN"
DC.reverse["F_IN"] = 0x8001
if TYPE_CHECKING:
from .svchub import SvcHub
if True: # pylint: disable=using-constant-test
from typing import Any, Optional, Union
if os.getenv("PRTY_MODSPEC"):
from inspect import getsourcefile
print("PRTY_MODSPEC: dnslib:", getsourcefile(A))
MDNS4 = "224.0.0.251"
MDNS6 = "ff02::fb"
@ -75,7 +109,7 @@ class MDNS(MCast):
self.ngen = ngen
self.ttl = 300
if not self.args.zm_nwa_1:
if not self.args.zm_nwa_1 and DNS_VND:
set_avahi_379()
zs = self.args.zm_fqdn or (self.args.name + ".local")

View file

@ -200,9 +200,10 @@ def au_unpk(
except Exception as ex:
if ret:
t = "failed to decompress audio file %r: %r"
t = "failed to decompress file %r: %r"
log(t % (abspath, ex))
wunlink(log, ret, vn.flags if vn else VF_CAREFUL)
return ""
return abspath
@ -422,10 +423,17 @@ def get_cover_from_epub(log: "NamedLogger", abspath: str) -> Optional[IO[bytes]]
# This might be an EPUB2 file, try the legacy way of specifying covers
coverimage_path = _get_cover_from_epub2(log, package_root, package_ns)
if not coverimage_path:
raise Exception("no cover inside epub")
# This url is either absolute (in the .epub) or relative to the package document
adjusted_cover_path = urljoin(rootfile_path, coverimage_path)
return z.open(adjusted_cover_path)
try:
return z.open(adjusted_cover_path)
except KeyError:
t = "epub: cover specified in package document, but doesn't exist: %s"
log(t % (adjusted_cover_path,))
def _get_cover_from_epub2(
@ -643,6 +651,9 @@ class MTag(object):
return self._get(abspath)
ap = au_unpk(self.log, self.args.au_unpk, abspath)
if not ap:
return {}
ret = self._get(ap)
if ap != abspath:
wunlink(self.log, ap, VF_CAREFUL)
@ -748,6 +759,9 @@ class MTag(object):
ap = abspath
ret: dict[str, Any] = {}
if not ap:
return ret
for tagname, parser in sorted(parsers.items(), key=lambda x: (x[1].pri, x[0])):
try:
cmd = [parser.bin, ap]

112
copyparty/qrkode.py Normal file
View file

@ -0,0 +1,112 @@
# coding: utf-8
from __future__ import print_function, unicode_literals
import os
try:
if os.getenv("PRTY_SYS_ALL") or os.getenv("PRTY_SYS_QRCG"):
raise ImportError()
from .stolen.qrcodegen import QrCode
qrgen = QrCode.encode_binary
VENDORED = True
except ImportError:
VENDORED = False
from qrcodegen import QrCode
if os.getenv("PRTY_MODSPEC"):
from inspect import getsourcefile
print("PRTY_MODSPEC: qrcode:", getsourcefile(QrCode))
if True: # pylint: disable=using-constant-test
import typing
from typing import Any, Optional, Sequence, Union
if not VENDORED:
def _qrgen(data: Union[bytes, Sequence[int]]) -> "QrCode":
ret = None
V = QrCode.Ecc
for e in [V.HIGH, V.QUARTILE, V.MEDIUM, V.LOW]:
qr = QrCode.encode_binary(data, e)
qr.size = qr._size
qr.modules = qr._modules
if not ret or ret.size > qr.size:
ret = qr
return ret
qrgen = _qrgen
def qr2txt(qr: QrCode, zoom: int = 1, pad: int = 4) -> str:
tab = qr.modules
sz = qr.size
if sz % 2 and zoom == 1:
tab.append([False] * sz)
tab = [[False] * sz] * pad + tab + [[False] * sz] * pad
tab = [[False] * pad + x + [False] * pad for x in tab]
rows: list[str] = []
if zoom == 1:
for y in range(0, len(tab), 2):
row = ""
for x in range(len(tab[y])):
v = 2 if tab[y][x] else 0
v += 1 if tab[y + 1][x] else 0
row += " ▄▀█"[v]
rows.append(row)
else:
for tr in tab:
row = ""
for zb in tr:
row += ""[int(zb)] * 2
rows.append(row)
return "\n".join(rows)
def qr2png(
qr: QrCode,
zoom: int,
pad: int,
bg: Optional[tuple[int, int, int]],
fg: Optional[tuple[int, int, int]],
ap: str,
) -> None:
from PIL import Image
tab = qr.modules
sz = qr.size
psz = sz + pad * 2
if bg:
img = Image.new("RGB", (psz, psz), bg)
else:
img = Image.new("RGBA", (psz, psz), (0, 0, 0, 0))
fg = (fg[0], fg[1], fg[2], 255)
for y in range(sz):
for x in range(sz):
if tab[y][x]:
img.putpixel((x + pad, y + pad), fg)
if zoom != 1:
img = img.resize((sz * zoom, sz * zoom), Image.Resampling.NEAREST)
img.save(ap)
def qr2svg(qr: QrCode, border: int) -> str:
parts: list[str] = []
for y in range(qr.size):
sy = border + y
for x in range(qr.size):
if qr.modules[y][x]:
parts.append("M%d,%dh1v1h-1z" % (border + x, sy))
t = """\
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none">
<rect width="100%" height="100%" fill="#F7F7F7"/>
<path d="{1}" fill="#111111"/>
</svg>
"""
return t.format(qr.size + border * 2, " ".join(parts))

View file

@ -4,7 +4,7 @@
# https://github.com/nayuki/QR-Code-generator/blob/daa3114/python/qrcodegen.py
# the original ^ is extremely well commented so refer to that for explanations
# hacks: binary-only, auto-ecc, render, py2-compat
# hacks: binary-only, auto-ecc, py2-compat
from __future__ import print_function, unicode_literals
@ -173,52 +173,6 @@ class QrCode(object):
self._apply_mask(msk) # Apply the final choice of mask
self._draw_format_bits(msk) # Overwrite old format bits
def render(self, zoom=1, pad=4) -> str:
tab = self.modules
sz = self.size
if sz % 2 and zoom == 1:
tab.append([False] * sz)
tab = [[False] * sz] * pad + tab + [[False] * sz] * pad
tab = [[False] * pad + x + [False] * pad for x in tab]
rows: list[str] = []
if zoom == 1:
for y in range(0, len(tab), 2):
row = ""
for x in range(len(tab[y])):
v = 2 if tab[y][x] else 0
v += 1 if tab[y + 1][x] else 0
row += " ▄▀█"[v]
rows.append(row)
else:
for tr in tab:
row = ""
for zb in tr:
row += ""[int(zb)] * 2
rows.append(row)
return "\n".join(rows)
def to_png(self, zoom, pad, bg, fg, ap) -> None:
from PIL import Image
tab = self.modules
sz = self.size
psz = sz + pad * 2
if bg:
img = Image.new("RGB", (psz, psz), bg)
else:
img = Image.new("RGBA", (psz, psz), (0, 0, 0, 0))
fg = (fg[0], fg[1], fg[2], 255)
for y in range(sz):
for x in range(sz):
if tab[y][x]:
img.putpixel((x + pad, y + pad), fg)
if zoom != 1:
img = img.resize((sz * zoom, sz * zoom), Image.Resampling.NEAREST)
img.save(ap)
def _draw_function_patterns(self) -> None:
# Draw horizontal and vertical timing patterns
for i in range(self.size):
@ -613,20 +567,3 @@ def _get_bit(x: int, i: int) -> bool:
class DataTooLongError(ValueError):
pass
def qr2svg(qr: QrCode, border: int) -> str:
parts: list[str] = []
for y in range(qr.size):
sy = border + y
for x in range(qr.size):
if qr.modules[y][x]:
parts.append("M%d,%dh1v1h-1z" % (border + x, sy))
t = """\
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none">
<rect width="100%" height="100%" fill="#F7F7F7"/>
<path d="{1}" fill="#111111"/>
</svg>
"""
return t.format(qr.size + border * 2, " ".join(parts))

View file

@ -67,6 +67,7 @@ from .util import (
build_netmap,
expat_ver,
gzip,
html_escape,
load_ipr,
load_ipu,
lock_file,
@ -247,8 +248,8 @@ class SvcHub(object):
t = "WARNING: --th-ram-max is very small (%.2f GiB); will not be able to %s"
self.log("root", t % (args.th_ram_max, zs), 3)
if args.chpw and args.have_idp_hdrs:
t = "ERROR: user-changeable passwords is incompatible with IdP/identity-providers; you must disable either --chpw or --idp-h-usr"
if args.chpw and args.have_idp_hdrs and "pw" not in args.auth_ord.split(","):
t = "ERROR: user-changeable passwords is not compatible with your current configuration. Choose one of these options to fix it:\n option1: disable --chpw\n option2: remove all use of IdP features; --idp-*\n option3: change --auth-ord to something like pw,idp,ipu"
self.log("root", t, 1)
raise Exception(t)
@ -1171,6 +1172,13 @@ class SvcHub(object):
if len(al.tcolor) == 3: # fc5 => ffcc55
al.tcolor = "".join([x * 2 for x in al.tcolor])
if self.args.name_url:
zs = html_escape(self.args.name_url, True, True)
zs = '<a href="%s">%s</a>' % (zs, self.args.name)
else:
zs = self.args.name
self.args.name_html = zs
zs = al.u2sz
zsl = [x.strip() for x in zs.split(",")]
if len(zsl) not in (1, 3):

View file

@ -9,7 +9,7 @@ import time
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
from .cert import gencert
from .stolen.qrcodegen import QrCode, qr2svg
from .qrkode import QrCode, qr2png, qr2svg, qr2txt, qrgen
from .util import (
E_ACCESS,
E_ADDR_IN_USE,
@ -21,6 +21,7 @@ from .util import (
VF_CAREFUL,
Netdev,
atomic_move,
get_adapters,
min_ex,
sunpack,
termsize,
@ -456,8 +457,6 @@ class TcpSrv(object):
self._distribute_netdevs()
def detect_interfaces(self, listen_ips: list[str]) -> dict[str, Netdev]:
from .stolen.ifaddr import get_adapters
listen_ips = [x for x in listen_ips if not x.startswith(("unix:", "fd:"))]
nics = get_adapters(True)
@ -630,7 +629,7 @@ class TcpSrv(object):
pad = self.args.qrp
zoom = self.args.qrz
qrc = QrCode.encode_binary(btxt)
qrc = qrgen(btxt)
for zs in self.args.qr_file or []:
self._qr2file(qrc, zs)
@ -643,7 +642,7 @@ class TcpSrv(object):
except:
zoom = 1
qr = qrc.render(zoom, pad)
qr = qr2txt(qrc, zoom, pad)
if self.args.no_ansi:
return "{}\n{}".format(txt, qr)
@ -684,12 +683,12 @@ class TcpSrv(object):
if zoom not in (1, 2):
raise Exception("invalid zoom for qr.txt; must be 1 or 2")
with open(ap, "wb") as f:
f.write(qrc.render(zoom, pad).encode("utf-8"))
f.write(qr2txt(qrc, zoom, pad).encode("utf-8"))
elif ap.endswith(".svg"):
with open(ap, "wb") as f:
f.write(qr2svg(qrc, pad).encode("utf-8"))
else:
qrc.to_png(zoom, pad, self._h2i(bg), self._h2i(fg), ap)
qr2png(qrc, zoom, pad, self._h2i(bg), self._h2i(fg), ap)
def _h2i(self, hs):
try:

View file

@ -381,7 +381,7 @@ class ThumbSrv(object):
else:
ap_unpk = abspath
if not bos.path.exists(tpath):
if ap_unpk and not bos.path.exists(tpath):
tex = tpath.rsplit(".", 1)[-1]
want_mp3 = tex == "mp3"
want_opus = tex in ("opus", "owa", "caf")
@ -424,12 +424,14 @@ class ThumbSrv(object):
except:
pass
conv_ok = False
for fun in funs:
try:
if not png_ok and tpath.endswith(".png"):
raise Exception("png only allowed for waveforms")
fun(ap_unpk, ttpath, fmt, vn)
conv_ok = True
break
except Exception as ex:
msg = "%s could not create thumbnail of %r\n%s"
@ -451,16 +453,20 @@ class ThumbSrv(object):
except:
pass
if abspath != ap_unpk:
if abspath != ap_unpk and ap_unpk:
wunlink(self.log, ap_unpk, vn.flags)
try:
atomic_move(self.log, ttpath, tpath, vn.flags)
except Exception as ex:
if not os.path.exists(tpath):
if conv_ok and not os.path.exists(tpath):
t = "failed to move [%s] to [%s]: %r"
self.log(t % (ttpath, tpath, ex), 3)
pass
elif not conv_ok:
try:
open(tpath, "ab").close()
except:
pass
untemp = []
with self.mutex:
@ -682,7 +688,7 @@ class ThumbSrv(object):
return
c: Union[str, int] = "90"
t = "FFmpeg failed (probably a corrupt video file):\n"
t = "FFmpeg failed (probably a corrupt file):\n"
if (
(not self.args.th_ff_jpg or time.time() - int(self.args.th_ff_jpg) < 60)
and cmd[-1].lower().endswith(b".webp")

View file

@ -94,7 +94,7 @@ ICV_EXTS = set(zsg.split(","))
zsg = "3gp,asf,av1,avc,avi,flv,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,vob,webm,wmv"
VCV_EXTS = set(zsg.split(","))
zsg = "aif,aiff,alac,ape,flac,m4a,mp3,oga,ogg,opus,tak,tta,wav,wma,wv"
zsg = "aif,aiff,alac,ape,flac,m4a,mp3,oga,ogg,opus,tak,tta,wav,wma,wv,cbz,epub"
ACV_EXTS = set(zsg.split(","))
zsg = "nohash noidx xdev xvol"

View file

@ -55,7 +55,6 @@ from .__init__ import (
unicode,
)
from .__version__ import S_BUILD_DT, S_VERSION
from .stolen import surrogateescape
try:
from datetime import datetime, timezone
@ -81,6 +80,9 @@ except:
if PY2:
range = xrange # type: ignore
from .stolen import surrogateescape
surrogateescape.register_surrogateescape()
if sys.version_info >= (3, 7) or (
@ -136,6 +138,25 @@ try:
except:
pass
try:
if os.environ.get("PRTY_NO_IFADDR"):
raise Exception()
try:
if os.getenv("PRTY_SYS_ALL") or os.getenv("PRTY_SYS_IFADDR"):
raise ImportError()
from .stolen.ifaddr import get_adapters
except ImportError:
from ifaddr import get_adapters
HAVE_IFADDR = True
except:
HAVE_IFADDR = False
def get_adapters(include_unconfigured=False):
return []
try:
if os.environ.get("PRTY_NO_SQLITE"):
raise Exception()
@ -173,6 +194,11 @@ try:
except:
pass
if os.getenv("PRTY_MODSPEC"):
from inspect import getsourcefile
print("PRTY_MODSPEC: ifaddr:", getsourcefile(get_adapters))
if True: # pylint: disable=using-constant-test
import types
from collections.abc import Callable, Iterable
@ -269,7 +295,6 @@ RE_MEMAVAIL = re.compile("^MemAvailable:.* kB")
BOS_SEP = ("%s" % (os.sep,)).encode("ascii")
surrogateescape.register_surrogateescape()
if WINDOWS and PY2:
FS_ENCODING = "utf-8"
else:
@ -2928,8 +2953,6 @@ def read_socket_chunked(
def list_ips() -> list[str]:
from .stolen.ifaddr import get_adapters
ret: set[str] = set()
for nic in get_adapters():
for ipo in nic.ips:
@ -3606,11 +3629,13 @@ def retchk(
def _parsehook(
log: Optional["NamedLogger"], cmd: str
) -> tuple[str, bool, bool, bool, float, dict[str, Any], list[str]]:
) -> tuple[str, bool, bool, bool, bool, bool, float, dict[str, Any], list[str]]:
areq = ""
chk = False
fork = False
jtxt = False
imp = False
sin = False
wait = 0.0
tout = 0.0
kill = "t"
@ -3624,6 +3649,10 @@ def _parsehook(
fork = True
elif arg == "j":
jtxt = True
elif arg == "I":
imp = True
elif arg == "s":
sin = True
elif arg.startswith("w"):
wait = float(arg[1:])
elif arg.startswith("t"):
@ -3668,7 +3697,7 @@ def _parsehook(
argv[0] = os.path.expandvars(os.path.expanduser(argv[0]))
return areq, chk, fork, jtxt, wait, sp_ka, argv
return areq, chk, imp, fork, sin, jtxt, wait, sp_ka, argv
def runihook(
@ -3678,7 +3707,7 @@ def runihook(
vol: "VFS",
ups: list[tuple[str, int, int, str, str, str, int, str]],
) -> bool:
_, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
_, chk, _, fork, _, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
bcmd = [sfsenc(x) for x in acmd]
if acmd[0].endswith(".py"):
bcmd = [sfsenc(pybin)] + bcmd
@ -3857,7 +3886,7 @@ def _runhook(
txt: str,
) -> dict[str, Any]:
ret = {"rc": 0}
areq, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
areq, chk, imp, fork, sin, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
if areq:
for ch in areq:
if ch not in perms:
@ -3865,7 +3894,7 @@ def _runhook(
if log:
log(t % (uname, cmd, areq, perms))
return ret # fallthrough to next hook
if jtxt:
if imp or jtxt:
ja = {
"ap": ap,
"vp": vp,
@ -3879,6 +3908,10 @@ def _runhook(
"src": src,
"txt": txt,
}
if imp:
ja["log"] = log
mod = loadpy(acmd[0], False)
return mod.main(ja)
arg = json.dumps(ja)
else:
arg = txt or ap
@ -3889,7 +3922,11 @@ def _runhook(
raise Exception("zmq says %d" % (zi,))
return {"rc": 0, "stdout": zs}
acmd += [arg]
if sin:
sp_ka["sin"] = (arg + "\n").encode("utf-8", "replace")
else:
acmd += [arg]
if acmd[0].endswith(".py"):
acmd = [pybin] + acmd
@ -3974,7 +4011,7 @@ def runhook(
else:
ret[k] = v
except Exception as ex:
(log or print)("hook: {}".format(ex))
(log or print)("hook: %r, %s" % (ex, ex))
if ",c," in "," + cmd:
return {}
break

View file

@ -22,8 +22,9 @@ window.baguetteBox = (function () {
afterHide: null,
duringHide: null,
onChange: null,
readDirRtl: false,
},
overlay, slider, btnPrev, btnNext, btnHelp, btnAnim, btnRotL, btnRotR, btnSel, btnFull, btnVmode, btnClose,
overlay, slider, btnPrev, btnNext, btnHelp, btnAnim, btnRotL, btnRotR, btnSel, btnFull, btnVmode, btnReadDir, btnClose,
currentGallery = [],
currentIndex = 0,
isOverlayVisible = false,
@ -73,10 +74,10 @@ window.baguetteBox = (function () {
var touchEvent = e.touches[0] || e.changedTouches[0];
if (touchEvent.pageX - touch.startX > 40) {
touchFlag = true;
showPreviousImage();
showLeftImage();
} else if (touchEvent.pageX - touch.startX < -40) {
touchFlag = true;
showNextImage();
showRightImage();
} else if (touch.startY - touchEvent.pageY > 100) {
hideOverlay();
}
@ -114,9 +115,9 @@ window.baguetteBox = (function () {
scrollTimer = Date.now();
if (d > 0)
showNextImage();
showNextImageIgnoreReadDir();
else
showPreviousImage();
showPreviousImageIgnoreReadDir();
};
var trapFocusInsideOverlay = function (e) {
@ -212,6 +213,7 @@ window.baguetteBox = (function () {
'<div id="bbox-btns">' +
'<button id="bbox-help" type="button">?</button>' +
'<button id="bbox-anim" type="button" tt="a">-</button>' +
'<button id="bbox-readdir" type="button" tt="a">ltr</button>' +
'<button id="bbox-rotl" type="button">↶</button>' +
'<button id="bbox-rotr" type="button">↷</button>' +
'<button id="bbox-tsel" type="button">sel</button>' +
@ -229,6 +231,7 @@ window.baguetteBox = (function () {
btnNext = ebi('bbox-next');
btnHelp = ebi('bbox-help');
btnAnim = ebi('bbox-anim');
btnReadDir = ebi('bbox-readdir');
btnRotL = ebi('bbox-rotl');
btnRotR = ebi('bbox-rotr');
btnSel = ebi('bbox-tsel');
@ -301,9 +304,9 @@ window.baguetteBox = (function () {
else if (e.shiftKey && kl != "r")
return;
else if (k == "ArrowLeft" || k == "Left" || kl == "j")
showPreviousImage();
showLeftImage();
else if (k == "ArrowRight" || k == "Right" || kl == "l")
showNextImage();
showRightImage();
else if (k == "Escape" || k == "Esc")
hideOverlay();
else if (k == "Home")
@ -353,6 +356,21 @@ window.baguetteBox = (function () {
tt.show.call(this);
}
function toggleReadDir() {
var o = options,
next = options.readDirRtl ? "ltr" : "rtl";
swrite('greaddir', next);
slider.className = "no-transition";
options = {};
setOptions(o);
updateOffset(true);
window.getComputedStyle(slider).opacity; // force a restyle
slider.className = "";
if (tt.en)
tt.show.call(this);
}
function setVmode() {
var v = vid();
ebi('bbox-vmode').style.display = v ? '' : 'none';
@ -492,12 +510,13 @@ window.baguetteBox = (function () {
bind(document, 'fullscreenchange', onFSC);
bind(overlay, 'click', overlayClickHandler);
bind(overlay, 'wheel', overlayWheelHandler);
bind(btnPrev, 'click', showPreviousImage);
bind(btnNext, 'click', showNextImage);
bind(btnPrev, 'click', showLeftImage);
bind(btnNext, 'click', showRightImage);
bind(btnClose, 'click', hideOverlay);
bind(btnVmode, 'click', tglVmode);
bind(btnHelp, 'click', halp);
bind(btnAnim, 'click', anim);
bind(btnReadDir, 'click', toggleReadDir);
bind(btnRotL, 'click', rotl);
bind(btnRotR, 'click', rotr);
bind(btnSel, 'click', tglsel);
@ -515,12 +534,13 @@ window.baguetteBox = (function () {
unbind(document, 'fullscreenchange', onFSC);
unbind(overlay, 'click', overlayClickHandler);
unbind(overlay, 'wheel', overlayWheelHandler);
unbind(btnPrev, 'click', showPreviousImage);
unbind(btnNext, 'click', showNextImage);
unbind(btnPrev, 'click', showLeftImage);
unbind(btnNext, 'click', showRightImage);
unbind(btnClose, 'click', hideOverlay);
unbind(btnVmode, 'click', tglVmode);
unbind(btnHelp, 'click', halp);
unbind(btnAnim, 'click', anim);
unbind(btnReadDir, 'click', toggleReadDir);
unbind(btnRotL, 'click', rotl);
unbind(btnRotR, 'click', rotr);
unbind(btnSel, 'click', tglsel);
@ -571,6 +591,23 @@ window.baguetteBox = (function () {
btnAnim.textContent = ['⇄', '⮺', '⚡'][anims.indexOf(an)];
btnAnim.setAttribute('tt', 'animation: ' + an);
options.readDirRtl = sread('greaddir') === "rtl";
var msg;
if (options.readDirRtl) {
btnReadDir.innerText = "rtl";
msg = "browse from right to left";
slider.style.display = "flex";
slider.style.flexDirection = "row-reverse";
} else {
btnReadDir.innerText = "ltr";
msg = "browse from left to right";
slider.style.flexDirection = "";
slider.style.display = "block";
}
btnReadDir.setAttribute("tt", msg);
btnReadDir.setAttribute("aria-label", msg);
slider.style.transition = (options.animation === 'fadeIn' ? 'opacity .3s ease' :
options.animation === 'slideIn' ? '' : 'none');
@ -815,12 +852,24 @@ window.baguetteBox = (function () {
show_buttons(this.paused ? 1 : 0);
}
function showNextImage(e) {
function showRightImage(e) {
ev(e);
var dir = options.readDirRtl ? -1 : 1;
return show(currentIndex + dir);
}
function showLeftImage(e) {
ev(e);
var dir = options.readDirRtl ? 1 : -1;
return show(currentIndex + dir);
}
function showNextImageIgnoreReadDir(e) {
ev(e);
return show(currentIndex + 1);
}
function showPreviousImage(e) {
function showPreviousImageIgnoreReadDir(e) {
ev(e);
return show(currentIndex - 1);
}
@ -848,10 +897,10 @@ window.baguetteBox = (function () {
}
if (index < 0)
return bounceAnimation('left');
return bounceAnimation(options.readDirRtl ? 'right' : 'left');
if (index >= imagesElements.length)
return bounceAnimation('right');
return bounceAnimation(options.readDirRtl ? 'left' : 'right');
try {
vid().pause();
@ -1005,7 +1054,7 @@ window.baguetteBox = (function () {
function vidEnd() {
if (this == vid() && vnext)
showNextImage();
showNextImageIgnoreReadDir();
}
function setloop(side) {
@ -1066,11 +1115,12 @@ window.baguetteBox = (function () {
return false;
}
function updateOffset() {
var offset = -currentIndex * 100 + '%',
function updateOffset(noTransition) {
var dir = options.readDirRtl ? 1 : -1,
offset = dir * currentIndex * 100 + '%',
xform = slider.style.perspective !== undefined;
if (options.animation === 'fadeIn') {
if (options.animation === 'fadeIn' && !noTransition) {
slider.style.opacity = 0;
setTimeout(function () {
xform ?
@ -1116,10 +1166,10 @@ window.baguetteBox = (function () {
fx = x / (rc.right - rc.left);
if (fx < 0.3)
return showPreviousImage();
return showLeftImage();
if (fx > 0.7)
return showNextImage();
return showRightImage();
show_buttons('t');
@ -1175,8 +1225,8 @@ window.baguetteBox = (function () {
return {
run: run,
show: show,
showNext: showNextImage,
showPrevious: showPreviousImage,
showNext: showRightImage,
showPrevious: showLeftImage,
relseek: relseek,
urltime: urltime,
playpause: playpause,

View file

@ -887,6 +887,9 @@ html.y #path a:hover {
#srv_info2 span {
color: var(--srv-1);
}
#srv_info2 a {
padding: 0;
}
#srv_info2 {
display: none;
}
@ -1936,6 +1939,8 @@ html.y #tree.nowrap .ntree a+a:hover {
#rui td input[type="text"] {
width: 100%;
}
#rui #rn_n_d,
#rui #rn_n_s,
#shui td.exs input[type="text"] {
width: 3em;
}
@ -2140,6 +2145,7 @@ html.noscroll .sbar::-webkit-scrollbar {
width: 100%;
height: 100%;
text-align: center;
flex: none;
}
.full-image figure {
display: inline;
@ -2334,6 +2340,9 @@ html.y #bbox-overlay figcaption a {
0%, 100% {transform: scale(0)}
50% {transform: scale(1)}
}
.no-transition {
transition: none !important;
}

View file

@ -134,7 +134,6 @@
CGV = {{ cgv|tojson }},
TS = "{{ ts }}",
dtheme = "{{ dtheme }}",
srvinf = "{{ srv_info }}",
lang = "{{ lang }}",
dfavico = "{{ favico }}",
have_tags_idx = {{ have_tags_idx }},

View file

@ -13093,6 +13093,7 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext),
abrt_key = "",
can_shr = false,
rtt = null,
srvinf = "",
ldks = [],
dks = {},
dk, mp;
@ -15681,7 +15682,7 @@ var fileman = (function () {
hdel = !(have_del && has(perms, 'delete')),
hcut = !(have_mv && has(perms, 'move')),
hpst = !(have_mv && has(perms, 'write')),
hshr = !can_shr;
hshr = !can_shr || !get_evpath().indexOf(have_shr);
if (!(enren || endel || encut || enpst))
hren = hdel = hcut = hpst = true;
@ -15932,7 +15933,8 @@ var fileman = (function () {
r.rename = function (e) {
ev(e);
var sel = msel.getsel();
var sel = msel.getsel(),
all = msel.all;
if (!sel.length)
return toast.err(3, L.fr_emore);
@ -15942,11 +15944,16 @@ var fileman = (function () {
var f = [],
sn = ++r.sn,
base = vsplit(sel[0].vp)[0],
s2d = {},
mkeys;
r.f = f;
r.n_s = 1;
r.n_d = 1;
for (var a = 0; a < sel.length; a++) {
s2d[a] = all.indexOf(sel[a]);
var vp = sel[a].vp;
if (vp.endsWith('/'))
vp = vp.slice(0, -1);
@ -15956,9 +15963,10 @@ var fileman = (function () {
return toast.err(0, esc('bug:\n' + base + '\n' + vsp[0]));
var vars = ft2dict(ebi(sel[a].id).closest('tr'));
mkeys = vars[1].concat(vars[2]);
mkeys = [".n.d", ".n.s"].concat(vars[1], vars[2]);
var md = vars[0];
md[".n.s"] = md[".n.d"] = 0;
for (var k in md) {
if (!md.hasOwnProperty(k))
continue;
@ -16005,6 +16013,10 @@ var fileman = (function () {
'<tr><td>regex</td><td><input type="text" id="rn_re" ' + NOAC + ' tt="' + L.fr_re + '" placeholder="^[0-9]+[\\. ]+(.*) - (.*)" /></td></tr>',
'<tr><td>format</td><td><input type="text" id="rn_fmt" ' + NOAC + ' tt="' + L.fr_fmt + '" placeholder="[(artist) - ](title).(ext)" /></td></tr>',
'<tr><td>preset</td><td><select id="rn_pre"></select>',
'<tr><td>num0</td><td>',
'<code>n.d=</code><input type="text" id="rn_n_d" placeholder="1" ' + NOAC + ' /> &nbsp;',
'<code>n.s=</code><input type="text" id="rn_n_s" placeholder="1" ' + NOAC + ' />',
'</td></tr>',
'<button id="rn_pdel">❌ ' + L.fr_pdel + '</button>',
'<button id="rn_pnew">💾 ' + L.fr_pnew + '</button>',
'</td></tr>',
@ -16149,6 +16161,15 @@ var fileman = (function () {
};
spresets();
ebi('rn_n_s').oninput = function () {
r.n_s = parseInt(this.value || '1');
ifmt.oninput();
};
ebi('rn_n_d').oninput = function () {
r.n_d = parseInt(this.value || '1');
ifmt.oninput();
};
ire.onkeydown = ifmt.onkeydown = function (e) {
var k = (e.key || e.code) + '';
@ -16178,14 +16199,18 @@ var fileman = (function () {
for (var a = 0; a < f.length; a++) {
var m = re ? re.exec(f[a].ofn) : null,
d = f[a].md,
ok, txt = '';
d[".n.s"] = d["n.s"] = '' + (r.n_s + a);
d[".n.d"] = d["n.d"] = '' + (r.n_d + s2d[a]);
if (re && !m) {
txt = 'regex did not match';
ok = false;
}
else {
var ret = fmt_ren(m, f[a].md, fmt);
var ret = fmt_ren(m, d, fmt);
ok = ret[0];
txt = ret[1];
}
@ -19112,6 +19137,11 @@ var treectl = (function () {
if (o)
o.innerHTML = ebi('srv_info').innerHTML = '<span>' + srvinf + '</span>';
if (res.ufavico && (!favico.en || !ebi('icot').value)) {
while (qsr('head>link[rel~="icon"]')) { }
document.head.insertAdjacentHTML('beforeend', res.ufavico);
}
if (this.hpush && !showfile.active())
hist_push(this.top + (dk ? '?k=' + dk : ''));
@ -19336,6 +19366,7 @@ var treectl = (function () {
r.hydrate = function () {
qsr('#bbsw');
srvinf = ebi('srv_info').innerHTML.slice(6, -7);
if (ls0 === null) {
var xhr = new XHR();
xhr.open('GET', SR + '/?setck=js=y', true);

View file

@ -1040,6 +1040,8 @@ function humansize_fuzzy(b) {
if (b <= 1300000000) return "GD-ROM";
if (b <= 4700000000) return "DVD";
if (b <= 9400000000) return "DVD-DL";
if (b <= 25025000000) return "BluRei";
if (b <= 50050000000) return "BD-DL";
return "LTO";
}
var humansize_fmts = ['0', '1', '2', '2c', '3', '3c', '4', '4c', '5', '5c', 'fuzzy'];
@ -2296,9 +2298,6 @@ function xhrchk(xhr, prefix, e404, lvl, tag) {
if (xhr.status == 404)
return toast.err(0, prefix + e404 + suf, tag);
if (XC_CMSG[xhr.status])
errtxt = XC_CMSG[xhr.status];
if (!xhr.status && !errtxt)
return toast.err(0, prefix + L.xhr0);
@ -2317,5 +2316,8 @@ function xhrchk(xhr, prefix, e404, lvl, tag) {
document.body.appendChild(fr);
}
if (XC_CMSG[xhr.status] && (errtxt.indexOf('<html') + 1))
errtxt = XC_CMSG[xhr.status];
return fun(0, prefix + xhr.status + ": " + errtxt, tag);
}

View file

@ -1,3 +1,39 @@
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-0929-2310 `v1.19.15` merry christmas
## 🧪 new features
* #184 add various human-readable formats for filesizes 234eddec
* search for files by their identifier ("wark"/checksum) 4e38e408
* and those are displayed in file-listings now too 456addf2
* PUT-upload with header `Replace` will overwrite any existing files 397ed565
* xbu/xau hooks can reject uploads with a custom message df0fa9d1
* #855 mDNS options to change the announced http/https port a3d95067
* #473 #383 custom favicons per-volume (.ico/png/gif/svg) 470b5048
* doesn't seem to work in internet explorer... ah whatever, go next
## 🩹 bugfixes
* #849 create IdP-db for `--idp-store` when necessary 80ca7851
* #859 cbz-thumbnailing had an accidental dependency on FFmpeg 983865d9
* docs: misleading markdown-expansion example e187df28
## 🔧 other changes
* #851 show a huge warning when copyparty accidentally detects a failing HDD and/or filesystem-corruption during indexing 6912e867 eb5d767b
* #870 improved discord video embeds (thx @tsuza!) f0ecb083
* #858 prefer reflinks (not hardlinks) in the `-ss` security option 57650a21
* improved controlpanel action-buttons layout 9f46e4db
## 🌠 fun facts
* includes (a tiny bit of) code written at [koie ramen](https://a.ocv.me/pub/g/2025/09/PXL_20250925_151716836.jpg)
* [according to Biltema](https://a.ocv.me/pub/g/2025/09/PXL_20250927_160446367~2.jpg), september is an excellent time to start decorating for xmas
<img src="https://a.ocv.me/pub/stuff/padoru.gif" alt="padoru" /> <img src="https://a.ocv.me/pub/stuff/padoru.gif" alt="padoru" /> <img src="https://a.ocv.me/pub/stuff/padoru.gif" alt="padoru" />
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-0923-2247 `v1.19.14` Voile, the Magic Library

View file

@ -15,6 +15,7 @@
* [general](#general)
* [event hooks](#event-hooks) - on writing your own [hooks](../README.md#event-hooks)
* [hook effects](#hook-effects) - hooks can cause intentional side-effects
* [hook import](#hook-import) - the `I` flag runs the hook inside copyparty
* [assumptions](#assumptions)
* [mdns](#mdns)
* [sfx repack](#sfx-repack) - reduce the size of an sfx by removing features
@ -310,6 +311,14 @@ a subset of effect types are available for a subset of hook types,
to trigger indexing of files `/foo/1.txt` and `/foo/bar/2.txt`, a hook can `print(json.dumps({"idx":{"vp":["/foo/1.txt","/foo/bar/2.txt"]}}))` (and replace "idx" with "del" to delete instead)
* note: paths starting with `/` are absolute URLs, but you can also do `../3.txt` relative to the destination folder of each uploaded file
## hook import
the `I` flag runs the hook inside copyparty, which can be very useful and dangerous:
* around 140x faster because it doesn't need to launch a new subprocess
* the hook can intentionally (or accidentally) mess with copyparty's internals
* very easy to crash things if not careful
# assumptions

View file

@ -6,15 +6,15 @@ L: MIT
https://github.com/pallets/jinja/
C: 2007 Pallets
L: BSD 3-Clause
L: BSD-3-Clause
https://github.com/pallets/markupsafe/
C: 2010 Pallets
L: BSD 3-Clause
L: BSD-3-Clause
https://github.com/paulc/dnslib/
C: 2010-2017 Paul Chakravarti
L: BSD 2-Clause
L: BSD-2-Clause
https://github.com/pydron/ifaddr/
C: 2014 Stefan C. Mueller
@ -36,6 +36,10 @@ https://github.com/ahupp/python-magic/
C: 2001-2014 Adam Hupp
L: MIT
https://github.com/fusepy/fusepy
C: 2012 Giorgos Verigakis
L: ISC
--- client-side --- software ---
https://github.com/Daninet/hash-wasm/
@ -50,6 +54,10 @@ https://github.com/feimosi/baguetteBox.js/
C: 2017 Marek Grzybek
L: MIT
https://github.com/cure53/DOMPurify/
C: 2025 Cure53 / Mario Heiderich
L: Apache-2.0 or MPL-2.0
https://github.com/markedjs/marked/
C: 2018+, MarkedJS
C: 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
@ -68,8 +76,8 @@ L: MIT
https://github.com/adobe-fonts/source-code-pro/
C: 2010-2019 Adobe
L: SIL OFL 1.1
L: OFL-1.1
https://github.com/FortAwesome/Font-Awesome/
C: 2022 Fonticons, Inc.
L: SIL OFL 1.1
L: OFL-1.1

View file

@ -1,6 +1,19 @@
#!/bin/ash
set -ex
tmv() {
touch -r "$1" t
mv t "$1"
}
iawk() {
awk "$1" <"$2" >t
tmv "$2"
}
ised() {
sed -r "$1" <"$2" >t
tmv "$2"
}
# use zlib-ng if available
f=/z/base/zlib_ng-0.5.1-cp312-cp312-linux_$(cat /etc/apk/arch).whl
[ "$1" != min ] && [ -e $f ] && {
@ -38,8 +51,13 @@ rm -rf \
/tmp/pe-* /z/copyparty-sfx.py \
ensurepip pydoc_data turtle.py turtledemo lib2to3
cd /usr/lib/python3.*/site-packages/copyparty/
rm stolen/surrogateescape.py
iawk '/^[^ ]/{s=0}/^if not VENDORED:/{s=1}!s' qrkode.py
iawk '/^[^ ]/{s=0}/^ DNS_VND = False/{s=1;print" raise"}!s' mdns.py
# speedhack
sed -ri 's/os.environ.get\("PRTY_NO_IMPRESO"\)/"1"/' /usr/lib/python3.*/site-packages/copyparty/util.py
ised 's/os.environ.get\("PRTY_NO_IMPRESO"\)/"1"/' util.py
# drop bytecode
find / -xdev -name __pycache__ -print0 | xargs -0 rm -rf
@ -52,6 +70,12 @@ find -name __pycache__ |
grep -E 'ty/web/|/pycpar' |
tr '\n' '\0' | xargs -0 rm -rf
smoketest() {
# two-for-one:
# 1) smoketest copyparty even starts
# 2) build any bytecode we missed
@ -88,5 +112,95 @@ kill $pid; wait $pid
# output from -e2d
rm -rf .hist /cfg/copyparty
}
smoketest
[ "$1" == min ] && {
# shrink amd64 from 45.5 to 33.2 MiB
# libstdc++ is pulled in by libmpdec++ in libmpdec; keep libmpdec.so
cd /usr/lib ; rm -rf \
libmpdec++.so* \
libncurses* \
libpanelw* \
libreadline* \
libstdc++.so* \
--
cd /usr/lib/python3.*/lib-dynload/ ; rm -rf \
*audioop.* \
_asyncio.* \
_ctypes_test.* \
_curses* \
_test* \
_xx* \
ossaudio.* \
readline.* \
xx* \
--
# keep http/client for u2c
cd /usr/lib/python3.*/ ; rm -rf \
site-packages/*.dist-info \
aifc.py \
asyncio \
bdb.py \
cgi.py \
config-3.*/Makefile \
ctypes/macholib \
dbm \
difflib.py \
doctest.py \
email/_header_value_parser.py \
html \
http/cookiejar.* \
http/server.* \
imaplib.py \
importlib/resources \
mailbox.py \
nntplib.py \
pickletools.py \
pydoc.py \
smtplib.py \
statistics.py \
tomllib \
unittest \
urllib/request.* \
venv \
wsgiref \
xml/dom \
xml/sax \
xmlrpc \
--
set +x
find -iname '*.pyc' |
grep -viE 'tftpy' |
while IFS= read -r x; do
y="$(printf '%s\n' "$x" | sed -r 's`/__pycache__/([^/]+)\.cpython-312\.pyc$`/\1.py`')"
[ -e "$y" ] || continue
[ "$y" = "$x" ] && continue
rm "$y"
mv "$x" "${y}c"
done
find -iname __pycache__ -print0 | xargs -0 rm -rf --
rm -rf /a
set -x
smoketest
# printf '%s\n' 'FROM localhost/copyparty-min-amd64' 'COPY a /' 'RUN /bin/ash /a' >Dockerfile
# podman rmi localhost/m2 ; podman build --squash-all -t m2 . && podman images && podman run --rm -it localhost/m2 --exit=idx && podman images
}
# goodbye
exec rm innvikler.sh

View file

@ -16,6 +16,8 @@ lics = [
"MIT License",
"BSD 2-Clause License",
"BSD 3-Clause License",
"ISC License",
"Apache License v2.0",
"SIL Open Font License v1.1",
]

View file

@ -1,39 +1,3 @@
CERNZOYR
Crezvffvba gb hfr, pbcl, zbqvsl, naq/be qvfgevohgr guvf fbsgjner sbe nal checbfr jvgu be jvgubhg srr vf urerol tenagrq, cebivqrq gung gur nobir pbclevtug abgvpr naq guvf crezvffvba abgvpr nccrne va nyy pbcvrf.
Gur tbnyf bs gur Bcra Sbag Yvprafr (BSY) ner gb fgvzhyngr jbeyqjvqr qrirybczrag bs pbyynobengvir sbag cebwrpgf, gb fhccbeg gur sbag perngvba rssbegf bs npnqrzvp naq yvathvfgvp pbzzhavgvrf, naq gb cebivqr n serr naq bcra senzrjbex va juvpu sbagf znl or funerq naq vzcebirq va cnegarefuvc jvgu bguref.
Gur BSY nyybjf gur yvprafrq sbagf gb or hfrq, fghqvrq, zbqvsvrq naq erqvfgevohgrq serryl nf ybat nf gurl ner abg fbyq ol gurzfryirf. Gur sbagf, vapyhqvat nal qrevingvir jbexf, pna or ohaqyrq, rzorqqrq, erqvfgevohgrq naq/be fbyq jvgu nal fbsgjner cebivqrq gung nal erfreirq anzrf ner abg hfrq ol qrevingvir jbexf. Gur sbagf naq qrevingvirf, ubjrire, pnaabg or eryrnfrq haqre nal bgure glcr bs yvprafr. Gur erdhverzrag sbe sbagf gb erznva haqre guvf yvprafr qbrf abg nccyl gb nal qbphzrag perngrq hfvat gur sbagf be gurve qrevingvirf.
QRSVAVGVBAF
"Sbag Fbsgjner" ersref gb gur frg bs svyrf eryrnfrq ol gur Pbclevtug Ubyqre(f) haqre guvf yvprafr naq pyrneyl znexrq nf fhpu. Guvf znl vapyhqr fbhepr svyrf, ohvyq fpevcgf naq qbphzragngvba.
"Erfreirq Sbag Anzr" ersref gb nal anzrf fcrpvsvrq nf fhpu nsgre gur pbclevtug fgngrzrag(f).
"Bevtvany Irefvba" ersref gb gur pbyyrpgvba bs Sbag Fbsgjner pbzcbaragf nf qvfgevohgrq ol gur Pbclevtug Ubyqre(f).
"Zbqvsvrq Irefvba" ersref gb nal qrevingvir znqr ol nqqvat gb, qryrgvat, be fhofgvghgvat - va cneg be va jubyr - nal bs gur pbzcbaragf bs gur Bevtvany Irefvba, ol punatvat sbezngf be ol cbegvat gur Sbag Fbsgjner gb n arj raivebazrag.
"Nhgube" ersref gb nal qrfvtare, ratvarre, cebtenzzre, grpuavpny jevgre be bgure crefba jub pbagevohgrq gb gur Sbag Fbsgjner.
CREZVFFVBA & PBAQVGVBAF
Crezvffvba vf urerol tenagrq, serr bs punetr, gb nal crefba bognvavat n pbcl bs gur Sbag Fbsgjner, gb hfr, fghql, pbcl, zretr, rzorq, zbqvsl, erqvfgevohgr, naq fryy zbqvsvrq naq hazbqvsvrq pbcvrf bs gur Sbag Fbsgjner, fhowrpg gb gur sbyybjvat pbaqvgvbaf:
1) Arvgure gur Sbag Fbsgjner abe nal bs vgf vaqvivqhny pbzcbaragf, va Bevtvany be Zbqvsvrq Irefvbaf, znl or fbyq ol vgfrys.
2) Bevtvany be Zbqvsvrq Irefvbaf bs gur Sbag Fbsgjner znl or ohaqyrq, erqvfgevohgrq naq/be fbyq jvgu nal fbsgjner, cebivqrq gung rnpu pbcl pbagnvaf gur nobir pbclevtug abgvpr naq guvf yvprafr. Gurfr pna or vapyhqrq rvgure nf fgnaq-nybar grkg svyrf, uhzna-ernqnoyr urnqref be va gur nccebcevngr znpuvar-ernqnoyr zrgnqngn svryqf jvguva grkg be ovanel svyrf nf ybat nf gubfr svryqf pna or rnfvyl ivrjrq ol gur hfre.
3) Ab Zbqvsvrq Irefvba bs gur Sbag Fbsgjner znl hfr gur Erfreirq Sbag Anzr(f) hayrff rkcyvpvg jevggra crezvffvba vf tenagrq ol gur pbeerfcbaqvat Pbclevtug Ubyqre. Guvf erfgevpgvba bayl nccyvrf gb gur cevznel sbag anzr nf cerfragrq gb gur hfref.
4) Gur anzr(f) bs gur Pbclevtug Ubyqre(f) be gur Nhgube(f) bs gur Sbag Fbsgjner funyy abg or hfrq gb cebzbgr, raqbefr be nqiregvfr nal Zbqvsvrq Irefvba, rkprcg gb npxabjyrqtr gur pbagevohgvba(f) bs gur Pbclevtug Ubyqre(f) naq gur Nhgube(f) be jvgu gurve rkcyvpvg jevggra crezvffvba.
5) Gur Sbag Fbsgjner, zbqvsvrq be hazbqvsvrq, va cneg be va jubyr, zhfg or qvfgevohgrq ragveryl haqre guvf yvprafr, naq zhfg abg or qvfgevohgrq haqre nal bgure yvprafr. Gur erdhverzrag sbe sbagf gb erznva haqre guvf yvprafr qbrf abg nccyl gb nal qbphzrag perngrq hfvat gur Sbag Fbsgjner.
GREZVANGVBA
Guvf yvprafr orpbzrf ahyy naq ibvq vs nal bs gur nobir pbaqvgvbaf ner abg zrg.
QVFPYNVZRE
GUR SBAG FBSGJNER VF CEBIVQRQ "NF VF", JVGUBHG JNEENAGL BS NAL XVAQ, RKCERFF BE VZCYVRQ, VAPYHQVAT OHG ABG YVZVGRQ GB NAL JNEENAGVRF BS ZREPUNAGNOVYVGL, SVGARFF SBE N CNEGVPHYNE CHECBFR NAQ ABAVASEVATRZRAG BS PBCLEVTUG, CNGRAG, GENQRZNEX, BE BGURE EVTUG. VA AB RIRAG FUNYY GUR PBCLEVTUG UBYQRE OR YVNOYR SBE NAL PYNVZ, QNZNTRF BE BGURE YVNOVYVGL, VAPYHQVAT NAL TRARENY, FCRPVNY, VAQVERPG, VAPVQRAGNY, BE PBAFRDHRAGVNY QNZNTRF, JURGURE VA NA NPGVBA BS PBAGENPG, GBEG BE BGUREJVFR, NEVFVAT SEBZ, BHG BS GUR HFR BE VANOVYVGL GB HFR GUR SBAG FBSGJNER BE SEBZ BGURE QRNYVATF VA GUR SBAG FBSGJNER.
GUR FBSGJNER VF CEBIVQRQ "NF VF" NAQ VFP QVFPYNVZF NYY JNEENAGVRF JVGU ERTNEQ GB GUVF FBSGJNER VAPYHQVAT NYY VZCYVRQ JNEENAGVRF BS ZREPUNAGNOVYVGL NAQ SVGARFF. VA AB RIRAG FUNYY VFP OR YVNOYR SBE NAL FCRPVNY, QVERPG, VAQVERPG, BE PBAFRDHRAGVNY QNZNTRF BE NAL QNZNTRF JUNGFBRIRE ERFHYGVAT SEBZ YBFF BS HFR, QNGN BE CEBSVGF, JURGURE VA NA NPGVBA BS PBAGENPG, ARTYVTRAPR BE BGURE GBEGVBHF NPGVBA, NEVFVAT BHG BS BE VA PBAARPGVBA JVGU GUR HFR BE CRESBEZNAPR BS GUVF FBSGJNER.

53
scripts/lics/5.r13 Normal file
View file

@ -0,0 +1,53 @@
Ncnpur Yvprafr
Irefvba 2.0, Wnahnel 2004
uggc://jjj.ncnpur.bet/yvprafrf/
GREZF NAQ PBAQVGVBAF SBE HFR, ERCEBQHPGVBA, NAQ QVFGEVOHGVBA
1. Qrsvavgvbaf.
"Yvprafr" funyy zrna gur grezf naq pbaqvgvbaf sbe hfr, ercebqhpgvba, naq qvfgevohgvba nf qrsvarq ol Frpgvbaf 1 guebhtu 9 bs guvf qbphzrag.
"Yvprafbe" funyy zrna gur pbclevtug bjare be ragvgl nhgubevmrq ol gur pbclevtug bjare gung vf tenagvat gur Yvprafr.
"Yrtny Ragvgl" funyy zrna gur havba bs gur npgvat ragvgl naq nyy bgure ragvgvrf gung pbageby, ner pbagebyyrq ol, be ner haqre pbzzba pbageby jvgu gung ragvgl. Sbe gur checbfrf bs guvf qrsvavgvba, "pbageby" zrnaf (v) gur cbjre, qverpg be vaqverpg, gb pnhfr gur qverpgvba be znantrzrag bs fhpu ragvgl, jurgure ol pbagenpg be bgurejvfr, be (vv) bjarefuvc bs svsgl creprag (50%) be zber bs gur bhgfgnaqvat funerf, be (vvv) orarsvpvny bjarefuvc bs fhpu ragvgl.
"Lbh" (be "Lbhe") funyy zrna na vaqvivqhny be Yrtny Ragvgl rkrepvfvat crezvffvbaf tenagrq ol guvf Yvprafr.
"Fbhepr" sbez funyy zrna gur cersreerq sbez sbe znxvat zbqvsvpngvbaf, vapyhqvat ohg abg yvzvgrq gb fbsgjner fbhepr pbqr, qbphzragngvba fbhepr, naq pbasvthengvba svyrf.
"Bowrpg" sbez funyy zrna nal sbez erfhygvat sebz zrpunavpny genafsbezngvba be genafyngvba bs n Fbhepr sbez, vapyhqvat ohg abg yvzvgrq gb pbzcvyrq bowrpg pbqr, trarengrq qbphzragngvba, naq pbairefvbaf gb bgure zrqvn glcrf.
"Jbex" funyy zrna gur jbex bs nhgubefuvc, jurgure va Fbhepr be Bowrpg sbez, znqr ninvynoyr haqre gur Yvprafr, nf vaqvpngrq ol n pbclevtug abgvpr gung vf vapyhqrq va be nggnpurq gb gur jbex (na rknzcyr vf cebivqrq va gur Nccraqvk orybj).
"Qrevingvir Jbexf" funyy zrna nal jbex, jurgure va Fbhepr be Bowrpg sbez, gung vf onfrq ba (be qrevirq sebz) gur Jbex naq sbe juvpu gur rqvgbevny erivfvbaf, naabgngvbaf, rynobengvbaf, be bgure zbqvsvpngvbaf ercerfrag, nf n jubyr, na bevtvany jbex bs nhgubefuvc. Sbe gur checbfrf bs guvf Yvprafr, Qrevingvir Jbexf funyy abg vapyhqr jbexf gung erznva frcnenoyr sebz, be zreryl yvax (be ovaq ol anzr) gb gur vagresnprf bs, gur Jbex naq Qrevingvir Jbexf gurerbs.
"Pbagevohgvba" funyy zrna nal jbex bs nhgubefuvc, vapyhqvat gur bevtvany irefvba bs gur Jbex naq nal zbqvsvpngvbaf be nqqvgvbaf gb gung Jbex be Qrevingvir Jbexf gurerbs, gung vf vagragvbanyyl fhozvggrq gb Yvprafbe sbe vapyhfvba va gur Jbex ol gur pbclevtug bjare be ol na vaqvivqhny be Yrtny Ragvgl nhgubevmrq gb fhozvg ba orunys bs gur pbclevtug bjare. Sbe gur checbfrf bs guvf qrsvavgvba, "fhozvggrq" zrnaf nal sbez bs ryrpgebavp, ireony, be jevggra pbzzhavpngvba frag gb gur Yvprafbe be vgf ercerfragngvirf, vapyhqvat ohg abg yvzvgrq gb pbzzhavpngvba ba ryrpgebavp znvyvat yvfgf, fbhepr pbqr pbageby flfgrzf, naq vffhr genpxvat flfgrzf gung ner znantrq ol, be ba orunys bs, gur Yvprafbe sbe gur checbfr bs qvfphffvat naq vzcebivat gur Jbex, ohg rkpyhqvat pbzzhavpngvba gung vf pbafcvphbhfyl znexrq be bgurejvfr qrfvtangrq va jevgvat ol gur pbclevtug bjare nf "Abg n Pbagevohgvba."
"Pbagevohgbe" funyy zrna Yvprafbe naq nal vaqvivqhny be Yrtny Ragvgl ba orunys bs jubz n Pbagevohgvba unf orra erprvirq ol Yvprafbe naq fhofrdhragyl vapbecbengrq jvguva gur Jbex.
2. Tenag bs Pbclevtug Yvprafr. Fhowrpg gb gur grezf naq pbaqvgvbaf bs guvf Yvprafr, rnpu Pbagevohgbe urerol tenagf gb Lbh n crecrghny, jbeyqjvqr, aba-rkpyhfvir, ab-punetr, eblnygl-serr, veeribpnoyr pbclevtug yvprafr gb ercebqhpr, cercner Qrevingvir Jbexf bs, choyvpyl qvfcynl, choyvpyl cresbez, fhoyvprafr, naq qvfgevohgr gur Jbex naq fhpu Qrevingvir Jbexf va Fbhepr be Bowrpg sbez.
3. Tenag bs Cngrag Yvprafr. Fhowrpg gb gur grezf naq pbaqvgvbaf bs guvf Yvprafr, rnpu Pbagevohgbe urerol tenagf gb Lbh n crecrghny, jbeyqjvqr, aba-rkpyhfvir, ab-punetr, eblnygl-serr, veeribpnoyr (rkprcg nf fgngrq va guvf frpgvba) cngrag yvprafr gb znxr, unir znqr, hfr, bssre gb fryy, fryy, vzcbeg, naq bgurejvfr genafsre gur Jbex, jurer fhpu yvprafr nccyvrf bayl gb gubfr cngrag pynvzf yvprafnoyr ol fhpu Pbagevohgbe gung ner arprffnevyl vasevatrq ol gurve Pbagevohgvba(f) nybar be ol pbzovangvba bs gurve Pbagevohgvba(f) jvgu gur Jbex gb juvpu fhpu Pbagevohgvba(f) jnf fhozvggrq. Vs Lbh vafgvghgr cngrag yvgvtngvba ntnvafg nal ragvgl (vapyhqvat n pebff-pynvz be pbhagrepynvz va n ynjfhvg) nyyrtvat gung gur Jbex be n Pbagevohgvba vapbecbengrq jvguva gur Jbex pbafgvghgrf qverpg be pbagevohgbel cngrag vasevatrzrag, gura nal cngrag yvprafrf tenagrq gb Lbh haqre guvf Yvprafr sbe gung Jbex funyy grezvangr nf bs gur qngr fhpu yvgvtngvba vf svyrq.
4. Erqvfgevohgvba. Lbh znl ercebqhpr naq qvfgevohgr pbcvrf bs gur Jbex be Qrevingvir Jbexf gurerbs va nal zrqvhz, jvgu be jvgubhg zbqvsvpngvbaf, naq va Fbhepr be Bowrpg sbez, cebivqrq gung Lbh zrrg gur sbyybjvat pbaqvgvbaf:
(n) Lbh zhfg tvir nal bgure erpvcvragf bs gur Jbex be Qrevingvir Jbexf n pbcl bs guvf Yvprafr; naq
(o) Lbh zhfg pnhfr nal zbqvsvrq svyrf gb pneel cebzvarag abgvprf fgngvat gung Lbh punatrq gur svyrf; naq
(p) Lbh zhfg ergnva, va gur Fbhepr sbez bs nal Qrevingvir Jbexf gung Lbh qvfgevohgr, nyy pbclevtug, cngrag, genqrznex, naq nggevohgvba abgvprf sebz gur Fbhepr sbez bs gur Jbex, rkpyhqvat gubfr abgvprf gung qb abg cregnva gb nal cneg bs gur Qrevingvir Jbexf; naq
(q) Vs gur Jbex vapyhqrf n "ABGVPR" grkg svyr nf cneg bs vgf qvfgevohgvba, gura nal Qrevingvir Jbexf gung Lbh qvfgevohgr zhfg vapyhqr n ernqnoyr pbcl bs gur nggevohgvba abgvprf pbagnvarq jvguva fhpu ABGVPR svyr, rkpyhqvat gubfr abgvprf gung qb abg cregnva gb nal cneg bs gur Qrevingvir Jbexf, va ng yrnfg bar bs gur sbyybjvat cynprf: jvguva n ABGVPR grkg svyr qvfgevohgrq nf cneg bs gur Qrevingvir Jbexf; jvguva gur Fbhepr sbez be qbphzragngvba, vs cebivqrq nybat jvgu gur Qrevingvir Jbexf; be, jvguva n qvfcynl trarengrq ol gur Qrevingvir Jbexf, vs naq jurerire fhpu guveq-cnegl abgvprf abeznyyl nccrne. Gur pbagragf bs gur ABGVPR svyr ner sbe vasbezngvbany checbfrf bayl naq qb abg zbqvsl gur Yvprafr. Lbh znl nqq Lbhe bja nggevohgvba abgvprf jvguva Qrevingvir Jbexf gung Lbh qvfgevohgr, nybatfvqr be nf na nqqraqhz gb gur ABGVPR grkg sebz gur Jbex, cebivqrq gung fhpu nqqvgvbany nggevohgvba abgvprf pnaabg or pbafgehrq nf zbqvslvat gur Yvprafr.
Lbh znl nqq Lbhe bja pbclevtug fgngrzrag gb Lbhe zbqvsvpngvbaf naq znl cebivqr nqqvgvbany be qvssrerag yvprafr grezf naq pbaqvgvbaf sbe hfr, ercebqhpgvba, be qvfgevohgvba bs Lbhe zbqvsvpngvbaf, be sbe nal fhpu Qrevingvir Jbexf nf n jubyr, cebivqrq Lbhe hfr, ercebqhpgvba, naq qvfgevohgvba bs gur Jbex bgurejvfr pbzcyvrf jvgu gur pbaqvgvbaf fgngrq va guvf Yvprafr.
5. Fhozvffvba bs Pbagevohgvbaf. Hayrff Lbh rkcyvpvgyl fgngr bgurejvfr, nal Pbagevohgvba vagragvbanyyl fhozvggrq sbe vapyhfvba va gur Jbex ol Lbh gb gur Yvprafbe funyy or haqre gur grezf naq pbaqvgvbaf bs guvf Yvprafr, jvgubhg nal nqqvgvbany grezf be pbaqvgvbaf. Abgjvgufgnaqvat gur nobir, abguvat urerva funyy fhcrefrqr be zbqvsl gur grezf bs nal frcnengr yvprafr nterrzrag lbh znl unir rkrphgrq jvgu Yvprafbe ertneqvat fhpu Pbagevohgvbaf.
6. Genqrznexf. Guvf Yvprafr qbrf abg tenag crezvffvba gb hfr gur genqr anzrf, genqrznexf, freivpr znexf, be cebqhpg anzrf bs gur Yvprafbe, rkprcg nf erdhverq sbe ernfbanoyr naq phfgbznel hfr va qrfpevovat gur bevtva bs gur Jbex naq ercebqhpvat gur pbagrag bs gur ABGVPR svyr.
7. Qvfpynvzre bs Jneenagl. Hayrff erdhverq ol nccyvpnoyr ynj be nterrq gb va jevgvat, Yvprafbe cebivqrf gur Jbex (naq rnpu Pbagevohgbe cebivqrf vgf Pbagevohgvbaf) ba na "NF VF" ONFVF, JVGUBHG JNEENAGVRF BE PBAQVGVBAF BS NAL XVAQ, rvgure rkcerff be vzcyvrq, vapyhqvat, jvgubhg yvzvgngvba, nal jneenagvrf be pbaqvgvbaf bs GVGYR, ABA-VASEVATRZRAG, ZREPUNAGNOVYVGL, be SVGARFF SBE N CNEGVPHYNE CHECBFR. Lbh ner fbyryl erfcbafvoyr sbe qrgrezvavat gur nccebcevngrarff bs hfvat be erqvfgevohgvat gur Jbex naq nffhzr nal evfxf nffbpvngrq jvgu Lbhe rkrepvfr bs crezvffvbaf haqre guvf Yvprafr.
8. Yvzvgngvba bs Yvnovyvgl. Va ab rirag naq haqre ab yrtny gurbel, jurgure va gbeg (vapyhqvat artyvtrapr), pbagenpg, be bgurejvfr, hayrff erdhverq ol nccyvpnoyr ynj (fhpu nf qryvorengr naq tebffyl artyvtrag npgf) be nterrq gb va jevgvat, funyy nal Pbagevohgbe or yvnoyr gb Lbh sbe qnzntrf, vapyhqvat nal qverpg, vaqverpg, fcrpvny, vapvqragny, be pbafrdhragvny qnzntrf bs nal punenpgre nevfvat nf n erfhyg bs guvf Yvprafr be bhg bs gur hfr be vanovyvgl gb hfr gur Jbex (vapyhqvat ohg abg yvzvgrq gb qnzntrf sbe ybff bs tbbqjvyy, jbex fgbccntr, pbzchgre snvyher be znyshapgvba, be nal naq nyy bgure pbzzrepvny qnzntrf be ybffrf), rira vs fhpu Pbagevohgbe unf orra nqivfrq bs gur cbffvovyvgl bs fhpu qnzntrf.
9. Npprcgvat Jneenagl be Nqqvgvbany Yvnovyvgl. Juvyr erqvfgevohgvat gur Jbex be Qrevingvir Jbexf gurerbs, Lbh znl pubbfr gb bssre, naq punetr n srr sbe, npprcgnapr bs fhccbeg, jneenagl, vaqrzavgl, be bgure yvnovyvgl boyvtngvbaf naq/be evtugf pbafvfgrag jvgu guvf Yvprafr. Ubjrire, va npprcgvat fhpu boyvtngvbaf, Lbh znl npg bayl ba Lbhe bja orunys naq ba Lbhe fbyr erfcbafvovyvgl, abg ba orunys bs nal bgure Pbagevohgbe, naq bayl vs Lbh nterr gb vaqrzavsl, qrsraq, naq ubyq rnpu Pbagevohgbe unezyrff sbe nal yvnovyvgl vapheerq ol, be pynvzf nffregrq ntnvafg, fhpu Pbagevohgbe ol ernfba bs lbhe npprcgvat nal fhpu jneenagl be nqqvgvbany yvnovyvgl.

39
scripts/lics/6.r13 Normal file
View file

@ -0,0 +1,39 @@
CERNZOYR
Gur tbnyf bs gur Bcra Sbag Yvprafr (BSY) ner gb fgvzhyngr jbeyqjvqr qrirybczrag bs pbyynobengvir sbag cebwrpgf, gb fhccbeg gur sbag perngvba rssbegf bs npnqrzvp naq yvathvfgvp pbzzhavgvrf, naq gb cebivqr n serr naq bcra senzrjbex va juvpu sbagf znl or funerq naq vzcebirq va cnegarefuvc jvgu bguref.
Gur BSY nyybjf gur yvprafrq sbagf gb or hfrq, fghqvrq, zbqvsvrq naq erqvfgevohgrq serryl nf ybat nf gurl ner abg fbyq ol gurzfryirf. Gur sbagf, vapyhqvat nal qrevingvir jbexf, pna or ohaqyrq, rzorqqrq, erqvfgevohgrq naq/be fbyq jvgu nal fbsgjner cebivqrq gung nal erfreirq anzrf ner abg hfrq ol qrevingvir jbexf. Gur sbagf naq qrevingvirf, ubjrire, pnaabg or eryrnfrq haqre nal bgure glcr bs yvprafr. Gur erdhverzrag sbe sbagf gb erznva haqre guvf yvprafr qbrf abg nccyl gb nal qbphzrag perngrq hfvat gur sbagf be gurve qrevingvirf.
QRSVAVGVBAF
"Sbag Fbsgjner" ersref gb gur frg bs svyrf eryrnfrq ol gur Pbclevtug Ubyqre(f) haqre guvf yvprafr naq pyrneyl znexrq nf fhpu. Guvf znl vapyhqr fbhepr svyrf, ohvyq fpevcgf naq qbphzragngvba.
"Erfreirq Sbag Anzr" ersref gb nal anzrf fcrpvsvrq nf fhpu nsgre gur pbclevtug fgngrzrag(f).
"Bevtvany Irefvba" ersref gb gur pbyyrpgvba bs Sbag Fbsgjner pbzcbaragf nf qvfgevohgrq ol gur Pbclevtug Ubyqre(f).
"Zbqvsvrq Irefvba" ersref gb nal qrevingvir znqr ol nqqvat gb, qryrgvat, be fhofgvghgvat - va cneg be va jubyr - nal bs gur pbzcbaragf bs gur Bevtvany Irefvba, ol punatvat sbezngf be ol cbegvat gur Sbag Fbsgjner gb n arj raivebazrag.
"Nhgube" ersref gb nal qrfvtare, ratvarre, cebtenzzre, grpuavpny jevgre be bgure crefba jub pbagevohgrq gb gur Sbag Fbsgjner.
CREZVFFVBA & PBAQVGVBAF
Crezvffvba vf urerol tenagrq, serr bs punetr, gb nal crefba bognvavat n pbcl bs gur Sbag Fbsgjner, gb hfr, fghql, pbcl, zretr, rzorq, zbqvsl, erqvfgevohgr, naq fryy zbqvsvrq naq hazbqvsvrq pbcvrf bs gur Sbag Fbsgjner, fhowrpg gb gur sbyybjvat pbaqvgvbaf:
1) Arvgure gur Sbag Fbsgjner abe nal bs vgf vaqvivqhny pbzcbaragf, va Bevtvany be Zbqvsvrq Irefvbaf, znl or fbyq ol vgfrys.
2) Bevtvany be Zbqvsvrq Irefvbaf bs gur Sbag Fbsgjner znl or ohaqyrq, erqvfgevohgrq naq/be fbyq jvgu nal fbsgjner, cebivqrq gung rnpu pbcl pbagnvaf gur nobir pbclevtug abgvpr naq guvf yvprafr. Gurfr pna or vapyhqrq rvgure nf fgnaq-nybar grkg svyrf, uhzna-ernqnoyr urnqref be va gur nccebcevngr znpuvar-ernqnoyr zrgnqngn svryqf jvguva grkg be ovanel svyrf nf ybat nf gubfr svryqf pna or rnfvyl ivrjrq ol gur hfre.
3) Ab Zbqvsvrq Irefvba bs gur Sbag Fbsgjner znl hfr gur Erfreirq Sbag Anzr(f) hayrff rkcyvpvg jevggra crezvffvba vf tenagrq ol gur pbeerfcbaqvat Pbclevtug Ubyqre. Guvf erfgevpgvba bayl nccyvrf gb gur cevznel sbag anzr nf cerfragrq gb gur hfref.
4) Gur anzr(f) bs gur Pbclevtug Ubyqre(f) be gur Nhgube(f) bs gur Sbag Fbsgjner funyy abg or hfrq gb cebzbgr, raqbefr be nqiregvfr nal Zbqvsvrq Irefvba, rkprcg gb npxabjyrqtr gur pbagevohgvba(f) bs gur Pbclevtug Ubyqre(f) naq gur Nhgube(f) be jvgu gurve rkcyvpvg jevggra crezvffvba.
5) Gur Sbag Fbsgjner, zbqvsvrq be hazbqvsvrq, va cneg be va jubyr, zhfg or qvfgevohgrq ragveryl haqre guvf yvprafr, naq zhfg abg or qvfgevohgrq haqre nal bgure yvprafr. Gur erdhverzrag sbe sbagf gb erznva haqre guvf yvprafr qbrf abg nccyl gb nal qbphzrag perngrq hfvat gur Sbag Fbsgjner.
GREZVANGVBA
Guvf yvprafr orpbzrf ahyy naq ibvq vs nal bs gur nobir pbaqvgvbaf ner abg zrg.
QVFPYNVZRE
GUR SBAG FBSGJNER VF CEBIVQRQ "NF VF", JVGUBHG JNEENAGL BS NAL XVAQ, RKCERFF BE VZCYVRQ, VAPYHQVAT OHG ABG YVZVGRQ GB NAL JNEENAGVRF BS ZREPUNAGNOVYVGL, SVGARFF SBE N CNEGVPHYNE CHECBFR NAQ ABAVASEVATRZRAG BS PBCLEVTUG, CNGRAG, GENQRZNEX, BE BGURE EVTUG. VA AB RIRAG FUNYY GUR PBCLEVTUG UBYQRE OR YVNOYR SBE NAL PYNVZ, QNZNTRF BE BGURE YVNOVYVGL, VAPYHQVAT NAL TRARENY, FCRPVNY, VAQVERPG, VAPVQRAGNY, BE PBAFRDHRAGVNY QNZNTRF, JURGURE VA NA NPGVBA BS PBAGENPG, GBEG BE BGUREJVFR, NEVFVAT SEBZ, BHG BS GUR HFR BE VANOVYVGL GB HFR GUR SBAG FBSGJNER BE SEBZ BGURE QRNYVATF VA GUR SBAG FBSGJNER.

View file

@ -1,3 +1,3 @@
these are foss licenses in rot13 so scanners don't think copyparty isn't mit
1=mit 2=2bsd 3=3bsd 4=ofl
1=mit 2=2bsd 3=3bsd 4=isc 5=apache2 6=ofl

View file

@ -25,6 +25,7 @@ copyparty/metrics.py,
copyparty/mtag.py,
copyparty/multicast.py,
copyparty/pwhash.py,
copyparty/qrkode.py,
copyparty/res,
copyparty/res/__init__.py,
copyparty/res/COPYING.txt,

View file

@ -155,7 +155,7 @@ class Cfg(Namespace):
ex = "gid uid"
ka.update(**{k: -1 for k in ex.split()})
ex = "hash_mt hsortn qdel safe_dedup scan_pr_r scan_pr_s scan_st_r srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz ui_filesz unp_who"
ex = "hash_mt hsortn qdel safe_dedup scan_pr_r scan_pr_s scan_st_r srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz unp_who"
ka.update(**{k: 1 for k in ex.split()})
ex = "ac_convt au_vol dl_list du_iwho mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who ver_iwho zip_who"
@ -164,7 +164,7 @@ class Cfg(Namespace):
ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs"
ka.update(**{k: 0 for k in ex.split()})
ex = "ah_alg bname chdir chmod_f chpw_db doctitle df exit favico ipa html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts shr tcolor textfiles txt_eol ufavico unlist vname xff_src zipmaxt R RS SR"
ex = "ah_alg bname chdir chmod_f chpw_db doctitle df exit favico ipa html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts shr tcolor textfiles txt_eol ufavico ufavico_h unlist vname xff_src zipmaxt R RS SR"
ka.update(**{k: "" for k in ex.split()})
ex = "ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url spinner"
@ -223,6 +223,7 @@ class Cfg(Namespace):
th_x3="n",
u2sort="s",
u2ts="c",
ui_filesz="1",
unpost=600,
ver_who="all",
warksalt="hunter2",